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
- Pass render.SubjectRes by value
- Bind the Subject renderer only to subject-producing views
- Fix upstream: change fmt.Errorf("expected SubjectRes, but got %T", s) to pass o so the message names the real type
- 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
- Subject.Render asserts the value type: pass SubjectRes, not *SubjectRes
- Do not trust the type named in this error's message — upstream formats the receiver instead of the payload; log %T of your own payload
- Bind the Subject renderer only to subject-producing views
- Use comma-ok assertions at custom call sites
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
- expecting PolicyRes but got %T
- expected Unstructured, but got %T
- expected Unstructured, but got %T
- expected Unstructured, but got %T
- expecting a ForwardRes but got %T
AI-assisted analysis of derailed/k9s@2d3ccc6ba2 (2026-08-15).
Data as JSON: /api/errors/e73c25d2b70ebe2c.
Report an issue: GitHub.