{"record":{"id":"8d7b4784512b1df3","repo":"derailed/k9s","slug":"no-factory-in-context","errorCode":null,"errorMessage":"no factory in context","messagePattern":"no factory in context","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/dao/pod.go","lineNumber":216,"sourceCode":"\to, err := p.getFactory().Get(p.gvr, fqn, true, labels.Everything())\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tvar pod v1.Pod\n\terr = runtime.DefaultUnstructuredConverter.FromUnstructured(o.(*unstructured.Unstructured).Object, &pod)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn &pod, nil\n}\n\n// TailLogs tails a given container logs.\nfunc (p *Pod) TailLogs(ctx context.Context, opts *LogOptions) ([]LogChan, error) {\n\tfac, ok := ctx.Value(internal.KeyFactory).(*watch.Factory)\n\tif !ok {\n\t\treturn nil, errors.New(\"no factory in context\")\n\t}\n\to, err := fac.Get(p.gvr, opts.Path, true, labels.Everything())\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tvar po v1.Pod\n\tif err := runtime.DefaultUnstructuredConverter.FromUnstructured(o.(*unstructured.Unstructured).Object, &po); err != nil {\n\t\treturn nil, err\n\t}\n\tcoCounts := len(po.Spec.InitContainers) + len(po.Spec.Containers) + len(po.Spec.EphemeralContainers)\n\tif coCounts == 1 {\n\t\topts.SingleContainer = true\n\t}\n\n\touts := make([]LogChan, 0, coCounts)\n\tif co, ok := GetDefaultContainer(&po.ObjectMeta, &po.Spec); ok && !opts.AllContainers {\n\t\topts.DefaultContainer = co\n\t\treturn append(outs, tailLogs(ctx, p, opts)), nil","sourceCodeStart":198,"sourceCodeEnd":234,"githubUrl":"https://github.com/derailed/k9s/blob/2d3ccc6ba2ce98c3781bfc441bb3e884f072774f/internal/dao/pod.go#L198-L234","documentation":"Pod.TailLogs pulls the watch factory out of the request context before it can even fetch the pod: the value under internal.KeyFactory must be dynamically a *watch.Factory (internal/dao/pod.go:216). If the key is missing, nil, or holds a different Factory implementation, the assertion fails and 'no factory in context' is returned immediately. The app normally seeds this via model/log.go and view/workload.go, whose factory is *watch.Factory.","triggerScenarios":"Calling Pod.TailLogs with a context lacking internal.KeyFactory or holding a non-*watch.Factory value: embedding code passing context.Background(), unit tests using mock dao.Factory implementations, refactors wrapping the factory in another type, or log tailing invoked before the app factory is initialized. It fails before the pod lookup, so the path is irrelevant.","commonSituations":"Embedding k9s DAOs in a custom tool; tests with pegomock factories that do not wrap *watch.Factory; views refactored to build their own contexts; nil app factory during startup races.","solutions":["Seed the key before tailing: ctx = context.WithValue(ctx, internal.KeyFactory, appFactory) with appFactory of dynamic type *watch.Factory.","Route log tailing through internal/model/log.go or internal/view/workload.go which already inject KeyFactory.","In tests, build a real *watch.Factory as the dao test helpers do instead of a mock Factory.","Make sure the value stored is the concrete *watch.Factory, not a dao.Factory wrapper."],"exampleFix":"// before\nctx := context.Background()\nchans, err := podDAO.TailLogs(ctx, opts) // -> no factory in context\n// after\nctx := context.WithValue(context.Background(), internal.KeyFactory, appFactory) // *watch.Factory\nchans, err := podDAO.TailLogs(ctx, opts)","handlingStrategy":"type-guard","validationCode":"f, ok := ctx.Value(internal.KeyFactory).(*watch.Factory)\nif !ok || f == nil {\n    ctx = context.WithValue(ctx, internal.KeyFactory, appFactory) // *watch.Factory\n}\nchans, err := podDAO.TailLogs(ctx, opts)","typeGuard":"func watchFactoryFrom(ctx context.Context) (*watch.Factory, bool) {\n    f, ok := ctx.Value(internal.KeyFactory).(*watch.Factory)\n    return f, ok && f != nil\n}","tryCatchPattern":"if _, err := podDAO.TailLogs(ctx, opts); err != nil && strings.Contains(err.Error(), \"no factory in context\") {\n    return errors.New(\"log tailing requires *watch.Factory; seed internal.KeyFactory\")\n}","preventionTips":["Never call TailLogs with context.Background(); build ctx through view helpers that inject app.factory.","Ensure the KeyFactory value's dynamic type is exactly *watch.Factory, not a wrapper or mock.","In tests, construct a real *watch.Factory as dao test helpers do.","Fail fast on the factory assertion at the entry point with a descriptive message."],"tags":["kubernetes","go","context","watch-factory","logs"],"backgroundTag":null,"analyzedSha":"2d3ccc6ba2ce98c3781bfc441bb3e884f072774f","analyzedAt":"2026-08-15T16:09:14.432Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}