derailed/k9s · error
expecting a SubjectKind
Error message
expecting a SubjectKind
What it means
The Subject DAO backs the RBAC subject browser for users, groups and serviceaccounts. Because a subject's kind cannot be derived from cluster data, List requires it pre-seeded in the context under internal.KeySubjectKind as a string (internal/dao/rbac_subject.go:29); the stock views inject it via their context functions (view/user.go, view/group.go, view/sa.go). An absent key returns 'expecting a SubjectKind' before any cluster call is made.
Source
Thrown at internal/dao/rbac_subject.go:29
"github.com/derailed/k9s/internal/render"
"k8s.io/apimachinery/pkg/runtime"
)
var (
_ Accessor = (*Subject)(nil)
_ Nuker = (*Subject)(nil)
)
// Subject represents a subject model.
type Subject struct {
Resource
}
// List returns a collection of subjects.
func (s *Subject) List(ctx context.Context, _ string) ([]runtime.Object, error) {
kind, ok := ctx.Value(internal.KeySubjectKind).(string)
if !ok {
return nil, errors.New("expecting a SubjectKind")
}
crbs, err := s.listClusterRoleBindings(kind)
if err != nil {
return nil, err
}
rbs, err := s.listRoleBindings(kind)
if err != nil {
return nil, err
}
for _, rb := range rbs {
crbs = crbs.Upsert(rb)
}
oo := make([]runtime.Object, len(crbs))
for i, o := range crbs {View on GitHub (pinned to 2d3ccc6ba2)
Solutions
- Seed the kind before listing: ctx = context.WithValue(ctx, internal.KeySubjectKind, "User") with one of User|Group|ServiceAccount.
- When writing views, copy the contextFn pattern from internal/view/user.go, view/group.go and view/sa.go.
- Ensure the stored value is a plain string; other types fail the assertion.
- In tests, seed KeySubjectKind the way internal/dao tests seed other context keys.
Example fix
// before ctx := context.Background() subs, err := subjectDAO.List(ctx, "") // -> expecting a SubjectKind // after ctx := context.WithValue(context.Background(), internal.KeySubjectKind, "Group") subs, err := subjectDAO.List(ctx, "")
Defensive patterns
Strategy: validation
Validate before calling
kind := "User" // one of User|Group|ServiceAccount
if _, ok := ctx.Value(internal.KeySubjectKind).(string); !ok {
ctx = context.WithValue(ctx, internal.KeySubjectKind, kind)
}
subs, err := subjectDAO.List(ctx, "") Type guard
func subjectKindFrom(ctx context.Context) (string, bool) {
k, ok := ctx.Value(internal.KeySubjectKind).(string)
return k, ok && (k == "User" || k == "Group" || k == "ServiceAccount")
} Prevention
- Always seed internal.KeySubjectKind before calling Subject.List; kind is not derivable from the cluster.
- Reuse the contextFn pattern from view/user.go, view/group.go and view/sa.go in custom views.
- Keep the value a plain string with one of the three exact kinds.
- In unit tests, seed context keys exactly as the dao tests do.
When it happens
Trigger: Calling dao.Subject.List with a context that never had context.WithValue(ctx, internal.KeySubjectKind, kind) — direct DAO use in tests, plugins or embedded tools, or a custom view that forgot to mirror the contextFn pattern of the user/group/sa views. Storing a non-string value under the key fails the same assertion.
Common situations: Programmatic use of the DAO layer outside the TUI; refactored or custom RBAC views building their own context; unit tests calling Subject.List on context.Background().
Related errors
- no context for gvr found
- expecting context Path
- no screendump dir found in context
- no benchmark dir found in context
- no path specified in context
AI-assisted analysis of derailed/k9s@2d3ccc6ba2 (2026-08-15).
Data as JSON: /api/errors/21d4604acad20e0f.
Report an issue: GitHub.