{"record":{"id":"706ba187bcdc2a41","repo":"derailed/k9s","slug":"expecting-a-controller-resource-for-q","errorCode":null,"errorMessage":"expecting a controller resource for %q","messagePattern":"expecting a controller resource for %q","errorType":"console","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/view/pf_extender.go","lineNumber":102,"sourceCode":"\t}\n\n\tpf := NewPortForward(client.PfGVR)\n\tpf.SetContextFn(p.portForwardContext)\n\tif err := p.App().inject(pf, false); err != nil {\n\t\tp.App().Flash().Err(err)\n\t}\n\n\treturn nil\n}\n\nfunc (p *PortForwardExtender) fetchPodName(path string) (string, error) {\n\tres, err := dao.AccessorFor(p.App().factory, p.GVR())\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tctrl, ok := res.(dao.Controller)\n\tif !ok {\n\t\treturn \"\", fmt.Errorf(\"expecting a controller resource for %q\", p.GVR())\n\t}\n\n\treturn ctrl.Pod(path)\n}\n\nfunc (p *PortForwardExtender) portForwardContext(ctx context.Context) context.Context {\n\tif bc := p.App().BenchFile; bc != \"\" {\n\t\tctx = context.WithValue(ctx, internal.KeyBenchCfg, p.App().BenchFile)\n\t}\n\n\treturn context.WithValue(ctx, internal.KeyPath, p.GetTable().GetSelectedItem())\n}\n\n// ----------------------------------------------------------------------------\n// Helpers...\n\nfunc ensurePodPortFwdAllowed(factory dao.Factory, podName string) error {\n\tpod, err := fetchPod(factory, podName)","sourceCodeStart":84,"sourceCodeEnd":120,"githubUrl":"https://github.com/derailed/k9s/blob/2d3ccc6ba2ce98c3781bfc441bb3e884f072774f/internal/view/pf_extender.go#L84-L120","documentation":"internal/view/pf_extender.go's fetchPodName resolves the DAO for the viewer's GVR via dao.AccessorFor and type-asserts it to the dao.Controller interface (which provides Pod(path) to find the owning/controlled pod). If the registered accessor does not implement dao.Controller, the assertion fails and this error is returned with the GVR string. It signals that the port-forward extender was attached to a resource whose data-access object cannot map a resource path to a pod.","triggerScenarios":"Invoking the port-forward command (shift+f) from a viewer whose GVR's DAO is a generic/non-controller implementation — e.g. a custom resource added via k9s plugins/aliases whose accessor is dao.Generic, or any GVR registered in the DAO registry without a Pod(path) method.","commonSituations":"Extending k9s with custom CRD viewers and reusing PortForwardExtender on them; a k9s version bump where the dao.Controller interface gained methods a fork's custom DAO no longer satisfies; wiring the extender to a GVR that is not a pod controller (Deployment-like) by mistake.","solutions":["Port-forward from a viewer whose DAO implements dao.Controller (deployments, statefulsets, daemonsets, jobs, pods) instead of the failing GVR","If this is your custom resource, implement the dao.Controller interface (Pod(path) method) on its DAO so the extender can resolve the backing pod","Register the correct DAO in the dao registry (dao.AccessorFor must return a controller-capable accessor for that GVR)","As a fallback, find the pod behind the resource manually (kubectl get pods --show-labels) and port-forward from the pod view"],"exampleFix":"// before: extender attached to a GVR whose DAO is generic\nres, _ := dao.AccessorFor(p.App().factory, p.GVR())\nctrl, ok := res.(dao.Controller)\n// ok == false -> error\n\n// after: implement dao.Controller on the custom DAO\ntype MyCRD struct {\n  dao.Generic\n}\n\nfunc (d *MyCRD) Pod(path string) (string, error) {\n  // resolve owner -> pod name\n  return ownerPod(d.Factory(), path)\n}","handlingStrategy":"type-guard","validationCode":"// before calling fetchPodName, verify the accessor is controller-capable\nres, err := dao.AccessorFor(f, gvr)\nif err != nil { return err }\nif _, ok := res.(dao.Controller); !ok {\n    return fmt.Errorf(\"resource %s cannot resolve a pod; port-forward from a controller or pod view\", gvr)","typeGuard":"func asController(res dao.Resource) (dao.Controller, bool) {\n    c, ok := res.(dao.Controller)\n    return c, ok\n}\n\n// usage\nif ctrl, ok := asController(res); ok {\n    pod, err := ctrl.Pod(path)\n}","tryCatchPattern":"// Go: check the comma-ok assertion instead of panicking; degrade gracefully\nif ctrl, ok := res.(dao.Controller); ok {\n    if podName, err := ctrl.Pod(path); err == nil {\n        return startForward(v, podName, pts)\n    }\n} else {\n    log.Debug(\"accessor lacks Controller\", \"gvr\", gvr)\n}","preventionTips":["Only attach PortForwardExtender to viewers whose DAO is known to implement dao.Controller (deploy/sts/ds/job/pod)","Keep a compile-time interface check in custom DAOs: var _ dao.Controller = (*MyDAO)(nil)","Run the comma-ok assertion early at viewer init and disable the pf keybinding when unsupported"],"tags":["k9s","port-forward","dao","type-assertion","go"],"backgroundTag":null,"analyzedSha":"2d3ccc6ba2ce98c3781bfc441bb3e884f072774f","analyzedAt":"2026-08-15T16:09:14.432Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}