derailed/k9s · error
expecting a ForwardRes but got %T
Error message
expecting a ForwardRes but got %T
What it means
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.
Source
Thrown at internal/render/portforward.go:71
func (PortForward) Header(string) model1.Header {
return model1.Header{
model1.HeaderColumn{Name: "NAMESPACE"},
model1.HeaderColumn{Name: "NAME"},
model1.HeaderColumn{Name: "CONTAINER"},
model1.HeaderColumn{Name: "PORTS"},
model1.HeaderColumn{Name: "URL"},
model1.HeaderColumn{Name: "C"},
model1.HeaderColumn{Name: "N"},
model1.HeaderColumn{Name: "VALID", Attrs: model1.Attrs{Wide: true}},
model1.HeaderColumn{Name: "AGE", Attrs: model1.Attrs{Time: true}},
}
}
// Render renders a K8s resource to screen.
func (PortForward) Render(o any, _ string, r *model1.Row) error {
pf, ok := o.(ForwardRes)
if !ok {
return fmt.Errorf("expecting a ForwardRes but got %T", o)
}
ports := strings.Split(pf.Port(), ":")
r.ID = pf.ID()
ns, n := client.Namespaced(r.ID)
r.Fields = model1.Fields{
ns,
trimContainer(n),
pf.Container(),
pf.Port(),
UrlFor(pf.Config.Host, pf.Config.Path, ports[0], pf.Address()),
AsThousands(int64(pf.Config.C)),
AsThousands(int64(pf.Config.N)),
"",
ToAge(metav1.Time{Time: pf.Age()}),
}
View on GitHub (pinned to 2d3ccc6ba2)
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
Example fix
// before
err := pfRenderer.Render(&render.ForwardRes{Forwarder: f, Config: cfg}, ns, row)
// after
err := pfRenderer.Render(render.ForwardRes{Forwarder: f, Config: cfg}, ns, row) Defensive patterns
Strategy: type-guard
Validate before calling
// gate before invoking the port-forward renderer
if _, ok := o.(render.ForwardRes); !ok {
return fmt.Errorf("port-forward view requires render.ForwardRes value, got %T", o)
}
err := pfRenderer.Render(o, ns, row) Type guard
func isForwardRes(o any) bool {
_, ok := o.(render.ForwardRes)
return ok
} Try / catch
if err := pfRenderer.Render(o, ns, row); err != nil {
slog.Warn("port-forward row skipped", "type", fmt.Sprintf("%T", o), slogs.Error, err)
return nil // drop the row, keep the view alive; never panic the UI over one bad object
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- expected Unstructured, but got %T
- expected Unstructured, but got %T
- expecting PolicyRes but got %T
- expected ReferenceRes, but got %T
- expected Unstructured, but got %T
AI-assisted analysis of derailed/k9s@2d3ccc6ba2 (2026-08-15).
Data as JSON: /api/errors/4270617ccd383d29.
Report an issue: GitHub.