derailed/k9s · error

expecting a factory but got %T

Error message

expecting a factory but got %T

What it means

locatePods (internal/xray/svc.go:77) pulls the DAO factory from the render context (ctx.Value(internal.KeyFactory)) and asserts it to dao.Factory. The error means the xray render context was created without the factory installed — the traversal entry point normally injects it — so pod listing by service selector cannot proceed.

Source

Thrown at internal/xray/svc.go:77

	if root.IsLeaf() {
		return nil
	}
	gvr, nsID := client.NsGVR, client.FQN(client.ClusterScope, svc.Namespace)
	nsn := parent.Find(gvr, nsID)
	if nsn == nil {
		nsn = NewTreeNode(gvr, nsID)
		parent.Add(nsn)
	}
	nsn.Add(root)

	return nil
}

func (s *Service) locatePods(ctx context.Context, ns string, sel map[string]string) ([]runtime.Object, error) {
	f, ok := ctx.Value(internal.KeyFactory).(dao.Factory)
	if !ok {
		return nil, fmt.Errorf("expecting a factory but got %T", ctx.Value(internal.KeyFactory))
	}

	ll := make([]string, 0, len(sel))
	for k, v := range sel {
		ll = append(ll, fmt.Sprintf("%s=%s", k, v))
	}

	fsel, err := labels.ConvertSelectorToLabelsMap(strings.Join(ll, ","))
	if err != nil {
		return nil, err
	}

	return f.List(client.PodGVR, ns, false, fsel.AsSelector())
}

View on GitHub (pinned to 2d3ccc6ba2)

Solutions

  1. Inject the factory before rendering: ctx = context.WithValue(ctx, internal.KeyFactory, app.factory) at the top of the xray traversal
  2. In tests, pass the real or fake dao.Factory: ctx = context.WithValue(context.Background(), internal.KeyFactory, factory)
  3. Keep the whole render chain on the same derived context so the value survives recursion
  4. Read the %T in the message to confirm what (if anything) replaced the factory value

Example fix

// before
ctx := context.Background()
s.Render(ctx, "ns1", obj) // -> expecting a factory but got <nil>

// after
ctx := context.WithValue(context.Background(), internal.KeyFactory, app.factory)
s.Render(ctx, "ns1", obj)
Defensive patterns

Strategy: type-guard

Validate before calling

// verify the factory is in the context before listing pods
f, ok := ctx.Value(internal.KeyFactory).(dao.Factory)
if !ok {
    return nil, fmt.Errorf("xray requires internal.KeyFactory in the context; got %T", ctx.Value(internal.KeyFactory))
}

Type guard

func factoryFrom(ctx context.Context) (dao.Factory, bool) {
    f, ok := ctx.Value(internal.KeyFactory).(dao.Factory)
    return f, ok
}

Prevention

When it happens

Trigger: Calling a renderer that calls locatePods with a context not built by the xray command (context.Background() or a context missing internal.KeyFactory); refactors that drop the context.WithValue(ctx, internal.KeyFactory, factory) at traversal start.

Common situations: Custom xray renderers or extracted helper code reused outside the xray flow; unit tests constructing contexts by hand; refactors losing the KeyFactory injection.

Related errors


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