derailed/k9s · error

expected SubjectRes, but got %T

Error message

expected SubjectRes, but got %T

What it means

Subject.Render draws RBAC subject rows and requires a render.SubjectRes value ({Name, Kind, FirstLocation}) built by the subject collector. The comma-ok assertion fails for pointers and any other type, returning this error. Known bug worth knowing: the message formats the renderer receiver s with %T instead of the payload o, so it always reports '*render.Subject' regardless of what was actually passed — do not trust the named type when diagnosing.

Source

Thrown at internal/render/subject.go:41

		return tcell.ColorMediumSpringGreen
	}
}

// Header returns a header row.
func (Subject) Header(string) model1.Header {
	return model1.Header{
		model1.HeaderColumn{Name: "NAME"},
		model1.HeaderColumn{Name: "KIND"},
		model1.HeaderColumn{Name: "FIRST LOCATION"},
		model1.HeaderColumn{Name: "VALID", Attrs: model1.Attrs{Wide: true}},
	}
}

// Render renders a K8s resource to screen.
func (s Subject) Render(o any, _ string, r *model1.Row) error {
	res, ok := o.(SubjectRes)
	if !ok {
		return fmt.Errorf("expected SubjectRes, but got %T", s)
	}

	r.ID = res.Name
	r.Fields = model1.Fields{
		res.Name,
		res.Kind,
		res.FirstLocation,
		"",
	}

	return nil
}

// ----------------------------------------------------------------------------
// Helpers...

// SubjectRes represents a subject rule.
type SubjectRes struct {

View on GitHub (pinned to 2d3ccc6ba2)

Solutions

  1. Pass render.SubjectRes by value
  2. Bind the Subject renderer only to subject-producing views
  3. Fix upstream: change fmt.Errorf("expected SubjectRes, but got %T", s) to pass o so the message names the real type
  4. Use comma-ok guards in wrappers and skip mismatches

Example fix

// before (upstream bug: receiver s hides the real payload type)
return fmt.Errorf("expected SubjectRes, but got %T", s)
// after
return fmt.Errorf("expected SubjectRes, but got %T", o)
Defensive patterns

Strategy: type-guard

Validate before calling

// gate before invoking the subject renderer
if _, ok := o.(render.SubjectRes); !ok {
	return fmt.Errorf("subject view requires render.SubjectRes value, got %T", o)
}
err := subjRenderer.Render(o, ns, row)

Type guard

func isSubjectRes(o any) bool {
	_, ok := o.(render.SubjectRes)
	return ok
}

Try / catch

if err := subjRenderer.Render(o, ns, row); err != nil {
	slog.Warn("subject row skipped", "type", fmt.Sprintf("%T", o), slogs.Error, err) // note: error text upstream misreports the type
	return nil // drop the row, keep the view alive
}

Prevention

When it happens

Trigger: Calling Subject{}.Render with &SubjectRes{...} (pointer), unstructured objects, or foreign row types; wiring the subject renderer into views that do not produce SubjectRes rows. The misleading %T makes root-causing harder — reproduce with the actual payload to see the real type.

Common situations: Extending the RBAC subject views; plugins listing users/groups/serviceaccounts; refactors switching the subject collector to pointer output.

Related errors


AI-assisted analysis of derailed/k9s@2d3ccc6ba2 (2026-08-15). Data as JSON: /api/errors/e73c25d2b70ebe2c. Report an issue: GitHub.