{"record":{"id":"c525311a47b57419","repo":"derailed/k9s","slug":"expecting-context-wait","errorCode":null,"errorMessage":"expecting context Wait","messagePattern":"expecting context Wait","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/dao/cluster.go","lineNumber":116,"sourceCode":"\t}()\n\n\tres := make(Refs, 0, 10)\n\tfor refs := range out {\n\t\tres = append(res, refs...)\n\t}\n\n\treturn res, nil\n}\n\n// ScanForSARefs scans cluster resources for serviceaccount refs.\nfunc ScanForSARefs(ctx context.Context, f Factory) (Refs, error) {\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\treturn nil, errors.New(\"expecting context Wait\")\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.ScanSA(ctx, fqn, wait)\n\t\t\tif err != nil {\n\t\t\t\tslog.Error(\"ServiceAccount scan failed\",\n\t\t\t\t\tslogs.RefType, fmt.Sprintf(\"%T\", s),\n\t\t\t\t\tslogs.Error, err,\n\t\t\t\t)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tselect {","sourceCodeStart":98,"sourceCodeEnd":134,"githubUrl":"https://github.com/derailed/k9s/blob/2d3ccc6ba2ce98c3781bfc441bb3e884f072774f/internal/dao/cluster.go#L98-L134","documentation":"Returned by ScanForSARefs (internal/dao/cluster.go:116) when ctx.Value(internal.KeyWait) is not a bool. KeyWait controls whether reference scans use the informer cache with wait (blocking sync) or hit the API directly. Notably ScanForRefs only logs a warning for the same omission, but ScanForSARefs treats it as a hard error.","triggerScenarios":"Calling ScanForSARefs with KeyPath set but omitting context.WithValue(ctx, internal.KeyWait, b); storing a *bool or any non-bool under the key makes the assertion fail identically.","commonSituations":"Callers copying the ScanForRefs contract (where Wait is optional) into ScanForSARefs; refactors that default wait at the call site instead of the context; tests building minimal contexts.","solutions":["Always set KeyWait explicitly: context.WithValue(ctx, internal.KeyWait, false) for instant (cache-less) scans or true to wait for cache sync","Use one shared context-enrichment helper for both scans and include all keys even when optional elsewhere","Keep the value a raw bool, not a pointer or custom type"],"exampleFix":"// before\nctx := context.WithValue(ctx, internal.KeyPath, fqn)\nrefs, err := dao.ScanForSARefs(ctx, factory)\n\n// after\nctx = context.WithValue(ctx, internal.KeyWait, true)\nrefs, err := dao.ScanForSARefs(ctx, factory)","handlingStrategy":"validation","validationCode":"ctx = context.WithValue(ctx, internal.KeyWait, false) // or true to wait for cache sync\nrefs, err := dao.ScanForSARefs(ctx, factory)","typeGuard":null,"tryCatchPattern":"refs, err := dao.ScanForSARefs(ctx, factory)\nif err != nil && strings.Contains(err.Error(), \"expecting context Wait\") {\n    ctx = context.WithValue(ctx, internal.KeyWait, true)\n    refs, err = dao.ScanForSARefs(ctx, factory)\n}","preventionTips":["Always set KeyWait as a raw bool in scan contexts","Do not share a partial context between the two scans without re-enrichment","Encode the Wait contract in the helper's signature so omission cannot compile away"],"tags":["go","context","k9s","serviceaccount","dao"],"backgroundTag":null,"analyzedSha":"2d3ccc6ba2ce98c3781bfc441bb3e884f072774f","analyzedAt":"2026-08-15T16:09:14.432Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}