{"record":{"id":"ffdc3585ba358bcf","repo":"derailed/k9s","slug":"expected-unstructured-but-got-t-ffdc35","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/svc.go","lineNumber":47,"sourceCode":"\tmodel1.HeaderColumn{Name: \"VALID\", Attrs: model1.Attrs{Wide: true}},\n\tmodel1.HeaderColumn{Name: \"AGE\", Attrs: model1.Attrs{Time: true}},\n}\n\n// Service renders a K8s Service to screen.\ntype Service struct {\n\tBase\n}\n\n// Header returns a header row.\nfunc (s Service) Header(_ string) model1.Header {\n\treturn s.doHeader(defaultSVCHeader)\n}\n\n// Render renders a K8s resource to screen.\nfunc (s Service) 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 := s.defaultRow(raw, row); err != nil {\n\t\treturn err\n\t}\n\tif s.specs.isEmpty() {\n\t\treturn nil\n\t}\n\tcols, err := s.specs.realize(raw, defaultSVCHeader, row)\n\tcols.hydrateRow(row)\n\n\treturn err\n}\n\nfunc (s Service) defaultRow(raw *unstructured.Unstructured, r *model1.Row) error {\n\tvar svc v1.Service\n\terr := runtime.DefaultUnstructuredConverter.FromUnstructured(raw.Object, &svc)\n\tif err != nil {\n\t\treturn err","sourceCodeStart":29,"sourceCodeEnd":65,"githubUrl":"https://github.com/derailed/k9s/blob/2d3ccc6ba2ce98c3781bfc441bb3e884f072774f/internal/render/svc.go#L29-L65","documentation":"The Service (svc) renderer only accepts Kubernetes objects as *unstructured.Unstructured. Render's type assertion `o.(*unstructured.Unstructured)` failed, so the caller passed a different Go type (typed struct, metav1.TableRow, map, or a pointer-of-pointer variant). This is a wiring/programming error in the code that feeds the renderer, not a cluster or data problem.","triggerScenarios":"Calling Service.Render(o, ns, row) with anything other than *unstructured.Unstructured, e.g. a *corev1.Service, a metav1.TableRow coming from a server-side Table response, or a raw map[string]interface{}. Typically happens in custom ResourceViewers, Decorators, or after upgrading client-go where the informer/cache path starts delivering typed or Table objects.","commonSituations":"Writing a custom viewer that wraps objects before delegating to the svc renderer; switching a browser's data source from informer cache (unstructured) to metav1.Table (Server-Side Table print); version upgrades of k8s.io/client-go changing what the dynamic client returns.","solutions":["Convert the object before rendering: runtime.DefaultUnstructuredConverter.ToUnstructured(obj) then wrap in &unstructured.Unstructured{Object: m}.","If the pipeline carries metav1.Table rows, switch to the Table renderer (Table.Render) instead of Service.Render.","Fix the caller: pass the row's original object (from cache/informer) unmodified instead of a transformed copy.","Log %T at the call site to identify exactly which type is being fed in, then normalize the producer side."],"exampleFix":"// before\nerr := svc.Render(typedSvc, ns, row)\n\n// after\nm, _ := runtime.DefaultUnstructuredConverter.ToUnstructured(typedSvc)\nerr := svc.Render(&unstructured.Unstructured{Object: m}, ns, row)","handlingStrategy":"type-guard","validationCode":"u, ok := o.(*unstructured.Unstructured)\nif !ok {\n    m, conv := runtime.DefaultUnstructuredConverter.ToUnstructured(o)\n    if !conv { return fmt.Errorf(\"cannot convert %T to unstructured\", o) }\n    u = &unstructured.Unstructured{Object: m}\n}\nerr := svc.Render(u, ns, row)","typeGuard":"func asUnstructured(o any) (*unstructured.Unstructured, bool) {\n    switch v := o.(type) {\n    case *unstructured.Unstructured:\n        return v, true\n    case unstructured.Unstructured:\n        return &v, true\n    default:\n        return nil, false\n    }\n}","tryCatchPattern":"if err := svc.Render(obj, ns, row); err != nil {\n    if strings.Contains(err.Error(), \"expected Unstructured\") {\n    slog.Error(\"renderer type mismatch\", \"actualType\", fmt.Sprintf(\"%T\", obj))\n    }\n}","preventionTips":["Normalize objects to *unstructured.Unstructured at the data-source boundary (cache/informer) so all renderers get one type.","Add unit tests that feed each registered renderer the exact type its pipeline produces.","Log %T on renderer entry during development to catch wiring mismatches early."],"tags":["go","kubernetes","type-assertion","rendering","programmer-error"],"backgroundTag":null,"analyzedSha":"2d3ccc6ba2ce98c3781bfc441bb3e884f072774f","analyzedAt":"2026-08-15T16:09:14.432Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}