{"record":{"id":"131d5d2c30b8e529","repo":"derailed/k9s","slug":"expecting-a-context-factory","errorCode":null,"errorMessage":"expecting a context factory","messagePattern":"expecting a context factory","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/dao/ds.go","lineNumber":74,"sourceCode":"\n// TailLogs tail logs for all pods represented by this DaemonSet.\nfunc (d *DaemonSet) TailLogs(ctx context.Context, opts *LogOptions) ([]LogChan, error) {\n\tds, err := d.GetInstance(opts.Path)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif ds.Spec.Selector == nil || len(ds.Spec.Selector.MatchLabels) == 0 {\n\t\treturn nil, fmt.Errorf(\"no valid selector found on daemonset %q\", opts.Path)\n\t}\n\n\treturn podLogs(ctx, ds.Spec.Selector.MatchLabels, opts)\n}\n\nfunc podLogs(ctx context.Context, sel map[string]string, opts *LogOptions) ([]LogChan, error) {\n\tf, ok := ctx.Value(internal.KeyFactory).(*watch.Factory)\n\tif !ok {\n\t\treturn nil, errors.New(\"expecting a context factory\")\n\t}\n\tls, err := metav1.ParseToLabelSelector(toSelector(sel))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tlsel, err := metav1.LabelSelectorAsSelector(ls)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tns, _ := client.Namespaced(opts.Path)\n\too, err := f.List(client.PodGVR, ns, true, lsel)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\topts.MultiPods = true\n\n\tvar po Pod","sourceCodeStart":56,"sourceCodeEnd":92,"githubUrl":"https://github.com/derailed/k9s/blob/2d3ccc6ba2ce98c3781bfc441bb3e884f072774f/internal/dao/ds.go#L56-L92","documentation":"podLogs is the shared helper behind TailLogs for DaemonSets, Jobs and similar workloads: it lists pods by the workload's selector using a watch factory pulled from the request context (internal/dao/ds.go:74). The ctx value under internal.KeyFactory must be dynamically a *watch.Factory; if the key is missing, nil, or holds a different Factory implementation, the assertion fails and 'expecting a context factory' is returned before any cluster call is made.","triggerScenarios":"Invoking DaemonSet.TailLogs, Job.TailLogs (or podLogs directly) with a context that lacks internal.KeyFactory or carries a non-*watch.Factory value. The app normally injects *watch.Factory via model/log.go and view/workload.go; mock dao.Factory implementations (e.g. pegomock factories in tests) or embedding code passing context.Background() fail the assertion.","commonSituations":"Embedding k9s DAOs in another tool and calling TailLogs with a hand-built context; unit tests that use mock factories that do not implement or wrap *watch.Factory; refactors that replace the app factory with a wrapper type; calling log tailing before the app factory is initialized.","solutions":["Inject the watch factory before tailing: ctx = context.WithValue(ctx, internal.KeyFactory, appFactory) where appFactory is the *watch.Factory instance.","Route log tailing through the stock helpers (internal/model/log.go, internal/view/workload.go) which already seed KeyFactory.","In tests, build a real *watch.Factory (as the dao test factories do) instead of a mock dao.Factory.","Ensure the value's dynamic type is exactly *watch.Factory, not a wrapper implementing dao.Factory."],"exampleFix":"// before\nctx := context.Background()\nchans, err := dsDAO.TailLogs(ctx, opts) // -> expecting a context factory\n// after\nctx := context.WithValue(context.Background(), internal.KeyFactory, appFactory) // *watch.Factory\nchans, err := dsDAO.TailLogs(ctx, opts)","handlingStrategy":"type-guard","validationCode":"f, ok := ctx.Value(internal.KeyFactory).(*watch.Factory)\nif !ok || f == nil {\n    return fmt.Errorf(\"log tailing requires a *watch.Factory in context\")\n}","typeGuard":"func factoryFrom(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 := dsDAO.TailLogs(ctx, opts); err != nil && strings.Contains(err.Error(), \"context factory\") {\n    ctx = context.WithValue(ctx, internal.KeyFactory, appFactory) // *watch.Factory\n    _, err = dsDAO.TailLogs(ctx, opts)\n}","preventionTips":["Never call TailLogs/podLogs with context.Background().","Seed KeyFactory exactly as model/log.go and view/workload.go do, with dynamic type *watch.Factory.","In tests, use a real *watch.Factory rather than a mock dao.Factory.","Assert the factory out of the context once at the entry point and fail fast with a clear 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"}