{"record":{"id":"a3cd7f9000f07234","repo":"derailed/k9s","slug":"expected-unstructured-but-got-t-a3cd7f","errorCode":null,"errorMessage":"expected Unstructured, but got %T","messagePattern":"expected Unstructured, but got %T","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/render/rs.go","lineNumber":52,"sourceCode":"var defaultRSHeader = model1.Header{\n\tmodel1.HeaderColumn{Name: \"NAMESPACE\"},\n\tmodel1.HeaderColumn{Name: \"NAME\"},\n\tmodel1.HeaderColumn{Name: \"VS\", Attrs: model1.Attrs{VS: true}},\n\tmodel1.HeaderColumn{Name: \"DESIRED\", Attrs: model1.Attrs{Align: tview.AlignRight}},\n\tmodel1.HeaderColumn{Name: \"CURRENT\", Attrs: model1.Attrs{Align: tview.AlignRight}},\n\tmodel1.HeaderColumn{Name: \"READY\", Attrs: model1.Attrs{Align: tview.AlignRight}},\n\tmodel1.HeaderColumn{Name: \"CONTAINERS\", Attrs: model1.Attrs{Wide: true}},\n\tmodel1.HeaderColumn{Name: \"IMAGES\", Attrs: model1.Attrs{Wide: true}},\n\tmodel1.HeaderColumn{Name: \"SELECTOR\", Attrs: model1.Attrs{Wide: true}},\n\tmodel1.HeaderColumn{Name: \"VALID\", Attrs: model1.Attrs{Wide: true}},\n\tmodel1.HeaderColumn{Name: \"AGE\", Attrs: model1.Attrs{Time: true}},\n}\n\n// Render renders a K8s resource to screen.\nfunc (r ReplicaSet) Render(o any, _ string, row *model1.Row) error {\n\traw, ok := o.(*unstructured.Unstructured)\n\tif !ok {\n\t\treturn fmt.Errorf(\"expected Unstructured, but got %T\", o)\n\t}\n\tif err := r.defaultRow(raw, row); err != nil {\n\t\treturn err\n\t}\n\tif r.specs.isEmpty() {\n\t\treturn nil\n\t}\n\tcols, err := r.specs.realize(raw, defaultRSHeader, row)\n\tcols.hydrateRow(row)\n\n\treturn err\n}\n\nfunc (r ReplicaSet) defaultRow(raw *unstructured.Unstructured, row *model1.Row) error {\n\tvar rs appsv1.ReplicaSet\n\terr := runtime.DefaultUnstructuredConverter.FromUnstructured(raw.Object, &rs)\n\tif err != nil {\n\t\treturn err","sourceCodeStart":34,"sourceCodeEnd":70,"githubUrl":"https://github.com/derailed/k9s/blob/2d3ccc6ba2ce98c3781bfc441bb3e884f072774f/internal/render/rs.go#L34-L70","documentation":"ReplicaSet.Render implements the k9s renderer contract for replicaset rows: the object must be *unstructured.Unstructured so defaultRow can fill base columns and the custom-column engine (r.specs.realize against defaultRSHeader, including READY/CONTAINERS/IMAGES/SELECTOR) can walk the raw JSON. The comma-ok assertion fails and returns this error, printing the concrete %T, when any other Go type reaches the renderer — a wiring bug in the caller.","triggerScenarios":"Calling ReplicaSet{}.Render with a typed *appsv1.ReplicaSet, a map[string]any, a value unstructured.Unstructured, or a synthetic row type; binding the rs renderer to a different GVR in a custom view factory.","commonSituations":"Writing k9s plugins or custom views that register renderers per GVR; refactoring a workload view to feed typed client-go objects; unit tests with typed fixtures.","solutions":["Pass the informer-cached object untouched as *unstructured.Unstructured","Convert typed objects with runtime.DefaultUnstructuredConverter.ToUnstructured and wrap in &unstructured.Unstructured{Object: m}","Verify the view factory binds ReplicaSet only to the RS GVR","Guard custom call sites with a comma-ok check and skip the row on mismatch"],"exampleFix":"// before\ntyped := &appsv1.ReplicaSet{}\nerr := rsRenderer.Render(typed, ns, row)\n// after\nm, _ := runtime.DefaultUnstructuredConverter.ToUnstructured(typed)\nerr := rsRenderer.Render(&unstructured.Unstructured{Object: m}, ns, row)","handlingStrategy":"type-guard","validationCode":"// gate before invoking the ReplicaSet renderer\nif _, ok := o.(*unstructured.Unstructured); !ok {\n\treturn fmt.Errorf(\"rs view requires *unstructured.Unstructured, got %T\", o)\n}\nerr := rsRenderer.Render(o, ns, row)","typeGuard":"func isUnstructured(o any) bool {\n\t_, ok := o.(*unstructured.Unstructured)\n\treturn ok\n}","tryCatchPattern":"if err := rsRenderer.Render(o, ns, row); err != nil {\n\tslog.Warn(\"rs row skipped\", \"type\", fmt.Sprintf(\"%T\", o), slogs.Error, err)\n\treturn nil // drop the row, keep the view alive\n}","preventionTips":["Register each renderer with exactly one GVR in the view factory","Deliver informer-cache objects to Render untouched as *unstructured.Unstructured","Convert typed objects with runtime.DefaultUnstructuredConverter before rendering","Unit-test Render with the exact asserted type (pointer, not value)"],"tags":["kubernetes","go","type-assertion","workloads","replicaset","renderer","k9s"],"backgroundTag":null,"analyzedSha":"2d3ccc6ba2ce98c3781bfc441bb3e884f072774f","analyzedAt":"2026-08-15T16:09:14.432Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}