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

  1. Seed the kind before listing: ctx = context.WithValue(ctx, internal.KeySubjectKind, "User") with one of User|Group|ServiceAccount.
  2. When writing views, copy the contextFn pattern from internal/view/user.go, view/group.go and view/sa.go.
  3. Ensure the stored value is a plain string; other types fail the assertion.
  4. 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

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


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