{"record":{"id":"055cd1775f748a9b","repo":"derailed/k9s","slug":"expecting-a-treenode-but-got-t","errorCode":null,"errorMessage":"expecting a TreeNode but got %T","messagePattern":"expecting a TreeNode but got %T","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/xray/svc.go","lineNumber":39,"sourceCode":"// Service represents an xray renderer.\ntype Service struct{}\n\n// Render renders an xray node.\nfunc (s *Service) Render(ctx context.Context, ns string, o any) error {\n\traw, ok := o.(*unstructured.Unstructured)\n\tif !ok {\n\t\treturn fmt.Errorf(\"expected Unstructured, but got %T\", o)\n\t}\n\n\tvar svc v1.Service\n\terr := runtime.DefaultUnstructuredConverter.FromUnstructured(raw.Object, &svc)\n\tif err != nil {\n\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}","sourceCodeStart":21,"sourceCodeEnd":57,"githubUrl":"https://github.com/derailed/k9s/blob/2d3ccc6ba2ce98c3781bfc441bb3e884f072774f/internal/xray/svc.go#L21-L57","documentation":"The xray Service renderer (internal/xray/svc.go:39) retrieves the tree parent node from the render context (ctx.Value(KeyParent)) and asserts it to *TreeNode. The assertion fails when the context was not built by the xray tree traversal — i.e. Render was called without a parent already installed under KeyParent, or with a value of another type.","triggerScenarios":"Invoking xray Service.Render directly (tests, custom renderers) without the xray ContextBuilder that seeds KeyParent with a root *TreeNode; a context where KeyParent was overwritten with a different type.","commonSituations":"Writing custom xray renderers or reordering the render traversal; copying renderer code into tools that pass a plain context.Background(); refactors that drop the context.WithValue chain between parent and child renders.","solutions":["Ensure Render is always entered from the xray traversal that sets context.WithValue(ctx, KeyParent, parentNode) before recursing into children","In custom renderers, mirror the pattern: read parent, create your root node, put it as the new KeyParent for child renders","In unit tests, seed the context: ctx = context.WithValue(ctx, KeyParent, NewTreeNode(gvr, fqn))","Check the %T in the message to see what type actually occupied KeyParent"],"exampleFix":"// before: direct call\nctx := context.Background()\nsvc.Render(ctx, \"ns1\", obj) // -> expecting a TreeNode but got <nil>\n\n// after: provide the parent\nroot := NewTreeNode(client.AllGVR, \"cluster\")\nctx := context.WithValue(context.Background(), KeyParent, root)\nsvc.Render(ctx, \"ns1\", obj)","handlingStrategy":"type-guard","validationCode":"// validate the context value before rendering\nparent, ok := ctx.Value(KeyParent).(*TreeNode)\nif !ok {\n    return fmt.Errorf(\"xray render requires a KeyParent *TreeNode; got %T\", ctx.Value(KeyParent))\n}","typeGuard":"func parentFrom(ctx context.Context) (*TreeNode, bool) {\n    n, ok := ctx.Value(KeyParent).(*TreeNode)\n    return n, ok\n}","tryCatchPattern":null,"preventionTips":["Always enter xray renderers through the traversal that seeds KeyParent","In tests, build the context with context.WithValue(ctx, KeyParent, NewTreeNode(gvr, fqn))","Propagate the derived context (ctx = context.WithValue(ctx, KeyParent, node)) to child renders — never context.Background()"],"tags":["k9s","xray","context","type-assertion","go"],"backgroundTag":null,"analyzedSha":"2d3ccc6ba2ce98c3781bfc441bb3e884f072774f","analyzedAt":"2026-08-15T16:09:14.432Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}