{"record":{"id":"4270617ccd383d29","repo":"derailed/k9s","slug":"expecting-a-forwardres-but-got-t","errorCode":null,"errorMessage":"expecting a ForwardRes but got %T","messagePattern":"expecting a ForwardRes but got %T","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/render/portforward.go","lineNumber":71,"sourceCode":"func (PortForward) Header(string) model1.Header {\n\treturn model1.Header{\n\t\tmodel1.HeaderColumn{Name: \"NAMESPACE\"},\n\t\tmodel1.HeaderColumn{Name: \"NAME\"},\n\t\tmodel1.HeaderColumn{Name: \"CONTAINER\"},\n\t\tmodel1.HeaderColumn{Name: \"PORTS\"},\n\t\tmodel1.HeaderColumn{Name: \"URL\"},\n\t\tmodel1.HeaderColumn{Name: \"C\"},\n\t\tmodel1.HeaderColumn{Name: \"N\"},\n\t\tmodel1.HeaderColumn{Name: \"VALID\", Attrs: model1.Attrs{Wide: true}},\n\t\tmodel1.HeaderColumn{Name: \"AGE\", Attrs: model1.Attrs{Time: true}},\n\t}\n}\n\n// Render renders a K8s resource to screen.\nfunc (PortForward) Render(o any, _ string, r *model1.Row) error {\n\tpf, ok := o.(ForwardRes)\n\tif !ok {\n\t\treturn fmt.Errorf(\"expecting a ForwardRes but got %T\", o)\n\t}\n\n\tports := strings.Split(pf.Port(), \":\")\n\tr.ID = pf.ID()\n\tns, n := client.Namespaced(r.ID)\n\n\tr.Fields = model1.Fields{\n\t\tns,\n\t\ttrimContainer(n),\n\t\tpf.Container(),\n\t\tpf.Port(),\n\t\tUrlFor(pf.Config.Host, pf.Config.Path, ports[0], pf.Address()),\n\t\tAsThousands(int64(pf.Config.C)),\n\t\tAsThousands(int64(pf.Config.N)),\n\t\t\"\",\n\t\tToAge(metav1.Time{Time: pf.Age()}),\n\t}\n","sourceCodeStart":53,"sourceCodeEnd":89,"githubUrl":"https://github.com/derailed/k9s/blob/2d3ccc6ba2ce98c3781bfc441bb3e884f072774f/internal/render/portforward.go#L53-L89","documentation":"PortForward.Render implements the k9s renderer contract for the port-forward view: it requires the object handed to it to be a render.ForwardRes value — the synthetic row type (an embedded Forwarder plus BenchCfg) the port-forward browser builds per tunnel. The comma-ok assertion on the any-typed parameter fails and returns this error, printing the concrete Go type (%T) that actually arrived. A mismatch means the producer feeding this renderer emitted something other than ForwardRes, so the row cannot be drawn. Note the assertion matches the value type, so passing a *ForwardRes pointer also fails.","triggerScenarios":"Invoking PortForward{}.Render(o, ns, row) with o being a *ForwardRes pointer, a raw *v1.Pod, an *unstructured.Unstructured, or any custom row type; wiring the portforward renderer to a different GVR in a custom view factory; plugins that list port-forwards from their own data source and forward those objects straight into Render.","commonSituations":"Contributing a new port-forward browser or plugin; refactoring Forwarder aggregation so the view now wraps tunnels in a different struct; unit tests passing pod fixtures instead of ForwardRes; copy-pasting a renderer registration into a custom view without matching the payload type.","solutions":["Ensure the port-forward view passes render.ForwardRes by value (not a pointer) to Render","Fix the view factory mapping so PortForward renders only the port-forward view's rows","Log the %T from the error to find the producer emitting the wrong type and fix it there","In your own code, use the comma-ok assertion before calling Render and skip the row instead of erroring"],"exampleFix":"// before\nerr := pfRenderer.Render(&render.ForwardRes{Forwarder: f, Config: cfg}, ns, row)\n// after\nerr := pfRenderer.Render(render.ForwardRes{Forwarder: f, Config: cfg}, ns, row)","handlingStrategy":"type-guard","validationCode":"// gate before invoking the port-forward renderer\nif _, ok := o.(render.ForwardRes); !ok {\n\treturn fmt.Errorf(\"port-forward view requires render.ForwardRes value, got %T\", o)\n}\nerr := pfRenderer.Render(o, ns, row)","typeGuard":"func isForwardRes(o any) bool {\n\t_, ok := o.(render.ForwardRes)\n\treturn ok\n}","tryCatchPattern":"if err := pfRenderer.Render(o, ns, row); err != nil {\n\tslog.Warn(\"port-forward row skipped\", \"type\", fmt.Sprintf(\"%T\", o), slogs.Error, err)\n\treturn nil // drop the row, keep the view alive; never panic the UI over one bad object\n}","preventionTips":["Match value-vs-pointer exactly: PortForward.Render asserts the ForwardRes value type","Bind each renderer to exactly one view/GVR in the factory","Unit-test Render with the same fixture type production code emits","Use comma-ok assertions at every custom Render call site"],"tags":["kubernetes","go","type-assertion","port-forward","renderer","k9s"],"backgroundTag":null,"analyzedSha":"2d3ccc6ba2ce98c3781bfc441bb3e884f072774f","analyzedAt":"2026-08-15T16:09:14.432Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}