{"record":{"id":"efad13a3f512f712","repo":"derailed/k9s","slug":"expecting-context-path","errorCode":null,"errorMessage":"expecting context Path","messagePattern":"expecting context Path","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/dao/cluster.go","lineNumber":65,"sourceCode":"func scanners() map[*client.GVR]RefScanner {\n\treturn map[*client.GVR]RefScanner{\n\t\tclient.DpGVR:  new(Deployment),\n\t\tclient.DsGVR:  new(DaemonSet),\n\t\tclient.StsGVR: new(StatefulSet),\n\t\tclient.CjGVR:  new(CronJob),\n\t\tclient.JobGVR: new(Job),\n\t}\n}\n\n// ScanForRefs scans cluster resources for resource references.\nfunc ScanForRefs(ctx context.Context, f Factory) (Refs, error) {\n\trgvr, ok := ctx.Value(internal.KeyGVR).(*client.GVR)\n\tif !ok {\n\t\treturn nil, errors.New(\"expecting context GVR\")\n\t}\n\tfqn, ok := ctx.Value(internal.KeyPath).(string)\n\tif !ok {\n\t\treturn nil, errors.New(\"expecting context Path\")\n\t}\n\twait, ok := ctx.Value(internal.KeyWait).(bool)\n\tif !ok {\n\t\tslog.Warn(\"Expecting context Wait key. Using default\")\n\t}\n\n\tvar wg sync.WaitGroup\n\tout := make(chan Refs)\n\tfor gvr, scanner := range scanners() {\n\t\twg.Add(1)\n\t\tgo func(ctx context.Context, gvr *client.GVR, s RefScanner, out chan Refs, wait bool) {\n\t\t\tdefer wg.Done()\n\t\t\ts.Init(f, gvr)\n\t\t\trefs, err := s.Scan(ctx, rgvr, fqn, wait)\n\t\t\tif err != nil {\n\t\t\t\tslog.Error(\"Reference scan failed for\",\n\t\t\t\t\tslogs.RefType, fmt.Sprintf(\"%T\", s),\n\t\t\t\t\tslogs.Error, err,","sourceCodeStart":47,"sourceCodeEnd":83,"githubUrl":"https://github.com/derailed/k9s/blob/2d3ccc6ba2ce98c3781bfc441bb3e884f072774f/internal/dao/cluster.go#L47-L83","documentation":"Returned by ScanForRefs (internal/dao/cluster.go:65) when ctx.Value(internal.KeyPath) is not a string. KeyPath carries the FQN of the origin resource being scanned for (the value named fqn downstream, passed to each scanner's Scan(ctx, fqn, wait)). It is checked right after KeyGVR; missing it aborts before the goroutine fan-out.","triggerScenarios":"Calling ScanForRefs with KeyGVR set but omitting WithValue(ctx, internal.KeyPath, fqn); storing a non-string (e.g. a typed FQN struct) under the key.","commonSituations":"Same class as [14]: direct DAO invocation from tests/tools; context built by helper that only forwards some keys; refactor renamed the path key constant and call sites drifted.","solutions":["Always inject KeyPath alongside KeyGVR: context.WithValue(ctx, internal.KeyPath, fqn) with fqn a plain string","Centralize context construction in one helper so the three keys (GVR, Path, Wait) are never partially set","Add a unit test asserting ScanForRefs succeeds with the helper-built context"],"exampleFix":"// before\nctx := context.WithValue(ctx, internal.KeyGVR, gvr)\nrefs, err := dao.ScanForRefs(ctx, factory)\n\n// after\nctx = context.WithValue(ctx, internal.KeyPath, client.FQN(ns, name))\nrefs, err := dao.ScanForRefs(ctx, factory)","handlingStrategy":"validation","validationCode":"if _, ok := ctx.Value(internal.KeyPath).(string); !ok {\n    ctx = context.WithValue(ctx, internal.KeyPath, fqn)\n}\nrefs, err := dao.ScanForRefs(ctx, factory)","typeGuard":"func hasPathKey(ctx context.Context) bool {\n    _, ok := ctx.Value(internal.KeyPath).(string)\n    return ok\n}","tryCatchPattern":"refs, err := dao.ScanForRefs(ctx, factory)\nif err != nil && strings.Contains(err.Error(), \"expecting context Path\") {\n    ctx = context.WithValue(ctx, internal.KeyPath, fqn)\n    refs, err = dao.ScanForRefs(ctx, factory)\n}","preventionTips":["Treat KeyGVR/KeyPath/KeyWait as one bundle — set them together","Keep the FQN a plain string (client.FQN output)","Route ref scans through the view layer that owns context assembly"],"tags":["go","context","k9s","references","dao"],"backgroundTag":null,"analyzedSha":"2d3ccc6ba2ce98c3781bfc441bb3e884f072774f","analyzedAt":"2026-08-15T16:09:14.432Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}