derailed/k9s · error
no factory in context
Error message
no factory in context
What it means
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.
Source
Thrown at internal/dao/pod.go:216
o, err := p.getFactory().Get(p.gvr, fqn, true, labels.Everything())
if err != nil {
return nil, err
}
var pod v1.Pod
err = runtime.DefaultUnstructuredConverter.FromUnstructured(o.(*unstructured.Unstructured).Object, &pod)
if err != nil {
return nil, err
}
return &pod, nil
}
// TailLogs tails a given container logs.
func (p *Pod) TailLogs(ctx context.Context, opts *LogOptions) ([]LogChan, error) {
fac, ok := ctx.Value(internal.KeyFactory).(*watch.Factory)
if !ok {
return nil, errors.New("no factory in context")
}
o, err := fac.Get(p.gvr, opts.Path, true, labels.Everything())
if err != nil {
return nil, err
}
var po v1.Pod
if err := runtime.DefaultUnstructuredConverter.FromUnstructured(o.(*unstructured.Unstructured).Object, &po); err != nil {
return nil, err
}
coCounts := len(po.Spec.InitContainers) + len(po.Spec.Containers) + len(po.Spec.EphemeralContainers)
if coCounts == 1 {
opts.SingleContainer = true
}
outs := make([]LogChan, 0, coCounts)
if co, ok := GetDefaultContainer(&po.ObjectMeta, &po.Spec); ok && !opts.AllContainers {
opts.DefaultContainer = co
return append(outs, tailLogs(ctx, p, opts)), nilView on GitHub (pinned to 2d3ccc6ba2)
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.
Example fix
// before ctx := context.Background() chans, err := podDAO.TailLogs(ctx, opts) // -> no factory in context // after ctx := context.WithValue(context.Background(), internal.KeyFactory, appFactory) // *watch.Factory chans, err := podDAO.TailLogs(ctx, opts)
Defensive patterns
Strategy: type-guard
Validate before calling
f, ok := ctx.Value(internal.KeyFactory).(*watch.Factory)
if !ok || f == nil {
ctx = context.WithValue(ctx, internal.KeyFactory, appFactory) // *watch.Factory
}
chans, err := podDAO.TailLogs(ctx, opts) Type guard
func watchFactoryFrom(ctx context.Context) (*watch.Factory, bool) {
f, ok := ctx.Value(internal.KeyFactory).(*watch.Factory)
return f, ok && f != nil
} Try / catch
if _, err := podDAO.TailLogs(ctx, opts); err != nil && strings.Contains(err.Error(), "no factory in context") {
return errors.New("log tailing requires *watch.Factory; seed internal.KeyFactory")
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- expecting a context factory
- expecting a job resource
- expecting a SubjectKind
- no context for gvr found
- expecting context Path
AI-assisted analysis of derailed/k9s@2d3ccc6ba2 (2026-08-15).
Data as JSON: /api/errors/8d7b4784512b1df3.
Report an issue: GitHub.