{"record":{"id":"6d4bafd95bee9728","repo":"derailed/k9s","slug":"expecting-a-factory-but-got-t","errorCode":null,"errorMessage":"expecting a factory but got %T","messagePattern":"expecting a factory but got %T","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/xray/svc.go","lineNumber":77,"sourceCode":"\n\tif root.IsLeaf() {\n\t\treturn nil\n\t}\n\tgvr, nsID := client.NsGVR, client.FQN(client.ClusterScope, svc.Namespace)\n\tnsn := parent.Find(gvr, nsID)\n\tif nsn == nil {\n\t\tnsn = NewTreeNode(gvr, nsID)\n\t\tparent.Add(nsn)\n\t}\n\tnsn.Add(root)\n\n\treturn nil\n}\n\nfunc (s *Service) locatePods(ctx context.Context, ns string, sel map[string]string) ([]runtime.Object, error) {\n\tf, ok := ctx.Value(internal.KeyFactory).(dao.Factory)\n\tif !ok {\n\t\treturn nil, fmt.Errorf(\"expecting a factory but got %T\", ctx.Value(internal.KeyFactory))\n\t}\n\n\tll := make([]string, 0, len(sel))\n\tfor k, v := range sel {\n\t\tll = append(ll, fmt.Sprintf(\"%s=%s\", k, v))\n\t}\n\n\tfsel, err := labels.ConvertSelectorToLabelsMap(strings.Join(ll, \",\"))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn f.List(client.PodGVR, ns, false, fsel.AsSelector())\n}\n","sourceCodeStart":59,"sourceCodeEnd":92,"githubUrl":"https://github.com/derailed/k9s/blob/2d3ccc6ba2ce98c3781bfc441bb3e884f072774f/internal/xray/svc.go#L59-L92","documentation":"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.","triggerScenarios":"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.","commonSituations":"Custom xray renderers or extracted helper code reused outside the xray flow; unit tests constructing contexts by hand; refactors losing the KeyFactory injection.","solutions":["Inject the factory before rendering: ctx = context.WithValue(ctx, internal.KeyFactory, app.factory) at the top of the xray traversal","In tests, pass the real or fake dao.Factory: ctx = context.WithValue(context.Background(), internal.KeyFactory, factory)","Keep the whole render chain on the same derived context so the value survives recursion","Read the %T in the message to confirm what (if anything) replaced the factory value"],"exampleFix":"// before\nctx := context.Background()\ns.Render(ctx, \"ns1\", obj) // -> expecting a factory but got <nil>\n\n// after\nctx := context.WithValue(context.Background(), internal.KeyFactory, app.factory)\ns.Render(ctx, \"ns1\", obj)","handlingStrategy":"type-guard","validationCode":"// verify the factory is in the context before listing pods\nf, ok := ctx.Value(internal.KeyFactory).(dao.Factory)\nif !ok {\n    return nil, fmt.Errorf(\"xray requires internal.KeyFactory in the context; got %T\", ctx.Value(internal.KeyFactory))\n}","typeGuard":"func factoryFrom(ctx context.Context) (dao.Factory, bool) {\n    f, ok := ctx.Value(internal.KeyFactory).(dao.Factory)\n    return f, ok\n}","tryCatchPattern":null,"preventionTips":["Inject the factory once at the top of the xray traversal: context.WithValue(ctx, internal.KeyFactory, factory)","Pass the same derived context down the whole render chain","In tests, seed internal.KeyFactory with a real or fake dao.Factory before rendering"],"tags":["k9s","xray","context","dao","type-assertion"],"backgroundTag":null,"analyzedSha":"2d3ccc6ba2ce98c3781bfc441bb3e884f072774f","analyzedAt":"2026-08-15T16:09:14.432Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}