{"record":{"id":"841a86e44324cbde","repo":"derailed/k9s","slug":"expecting-unstructured-but-got-t","errorCode":null,"errorMessage":"expecting *Unstructured but got %T","messagePattern":"expecting \\*Unstructured but got %T","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/xray/svc.go","lineNumber":52,"sourceCode":"\t\treturn err\n\t}\n\n\tparent, ok := ctx.Value(KeyParent).(*TreeNode)\n\tif !ok {\n\t\treturn fmt.Errorf(\"expecting a TreeNode but got %T\", ctx.Value(KeyParent))\n\t}\n\n\troot := NewTreeNode(client.SvcGVR, client.FQN(svc.Namespace, svc.Name))\n\too, err := s.locatePods(ctx, svc.Namespace, svc.Spec.Selector)\n\tif err != nil {\n\t\treturn err\n\t}\n\tctx = context.WithValue(ctx, KeyParent, root)\n\tvar re Pod\n\tfor _, o := range oo {\n\t\tp, ok := o.(*unstructured.Unstructured)\n\t\tif !ok {\n\t\t\treturn fmt.Errorf(\"expecting *Unstructured but got %T\", o)\n\t\t}\n\t\tif err := re.Render(ctx, ns, &render.PodWithMetrics{Raw: p}); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\troot.Extras[StatusKey] = OkStatus\n\n\tif root.IsLeaf() {\n\t\treturn nil\n\t}\n\tgvr, nsID := client.NsGVR, client.FQN(client.ClusterScope, svc.Namespace)\n\tnsn := parent.Find(gvr, nsID)\n\tif nsn == nil {\n\t\tnsn = NewTreeNode(gvr, nsID)\n\t\tparent.Add(nsn)\n\t}\n\tnsn.Add(root)\n","sourceCodeStart":34,"sourceCodeEnd":70,"githubUrl":"https://github.com/derailed/k9s/blob/2d3ccc6ba2ce98c3781bfc441bb3e884f072774f/internal/xray/svc.go#L34-L70","documentation":"After locating pods by service selector, the xray Service renderer (internal/xray/svc.go:52) asserts each returned runtime.Object to *unstructured.Unstructured before wrapping it in render.PodWithMetrics. The informer/lister backing the pod lookup is expected to serve unstructured objects; if it returns typed objects (or any other implementation), rendering aborts.","triggerScenarios":"A factory/informer cache configured with typed client-go informers for pods while xray expects unstructured ones; test doubles injecting fake runtime.Objects that are not *unstructured.Unstructured; forks mixing typed and unstructured DAOs.","commonSituations":"Custom k9s builds swapping informer factories; upgrading client-go where lister behavior changed; unit tests with generated mocks failing the assertion.","solutions":["Serve pods from an unstructured informer (dynamic client factory) as the standard k9s watch factory does","In tests, return &unstructured.Unstructured{...} from locatePods stubs instead of *corev1.Pod","Convert before rendering when a typed object is unavoidable: runtime.DefaultUnstructuredConverter.ToUnstructured(typedObj) then wrap","Check the %T output to identify which informer produced the typed object"],"exampleFix":"// before: typed pod from a typed informer\nvar o runtime.Object = typedPod // *corev1.Pod\np, ok := o.(*unstructured.Unstructured) // !ok -> error\n\n// after: convert\nu, err := runtime.DefaultUnstructuredConverter.ToUnstructured(typedPod)\nif err != nil { return err }\np := &unstructured.Unstructured{Object: u}","handlingStrategy":"type-guard","validationCode":"// verify informer output type before the render loop\nfor _, o := range oo {\n    if _, ok := o.(*unstructured.Unstructured); !ok {\n        return fmt.Errorf(\"informer returned %T; unstructured required\", o)\n    }\n}","typeGuard":"func asUnstructured(o runtime.Object) (*unstructured.Unstructured, bool) {\n    u, ok := o.(*unstructured.Unstructured)\n    return u, ok\n}","tryCatchPattern":"// comma-ok with conversion fallback for typed objects\nu, ok := o.(*unstructured.Unstructured)\nif !ok {\n    m, err := runtime.DefaultUnstructuredConverter.ToUnstructured(o)\n    if err != nil { return err }\n    u = &unstructured.Unstructured{Object: m}\n}","preventionTips":["Back pod lookups with dynamic/unstructured informers, not typed client-go informers","In unit tests, stub listers to return &unstructured.Unstructured{Object: map[string]interface{}{...}}","Assert object types at the DAO boundary so renderers can trust their inputs"],"tags":["k9s","xray","unstructured","informer","type-assertion"],"backgroundTag":null,"analyzedSha":"2d3ccc6ba2ce98c3781bfc441bb3e884f072774f","analyzedAt":"2026-08-15T16:09:14.432Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}