{"record":{"id":"9f4c6e60b409066c","repo":"gastownhall/beads","slug":"unexpected-node-type-t","errorCode":null,"errorMessage":"unexpected node type: %T","messagePattern":"unexpected node type: %T","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/query/evaluator.go","lineNumber":152,"sourceCode":"\tcase *ComparisonNode:\n\t\treturn e.applyComparison(n, filter)\n\tcase *AndNode:\n\t\tif err := e.buildFilter(n.Left, filter); err != nil {\n\t\t\treturn err\n\t\t}\n\t\treturn e.buildFilter(n.Right, filter)\n\tcase *NotNode:\n\t\treturn e.applyNot(n, filter)\n\tcase *OrNode:\n\t\t// Only reached for LabelsAny optimization\n\t\tlabels := e.collectOrLabels(n)\n\t\tif labels != nil {\n\t\t\tfilter.LabelsAny = append(filter.LabelsAny, labels...)\n\t\t\treturn nil\n\t\t}\n\t\treturn fmt.Errorf(\"OR not supported for this field combination\")\n\tdefault:\n\t\treturn fmt.Errorf(\"unexpected node type: %T\", node)\n\t}\n}\n\n// applyComparison applies a comparison to the filter.\nfunc (e *Evaluator) applyComparison(comp *ComparisonNode, filter *types.IssueFilter) error {\n\tswitch comp.Field {\n\tcase \"status\":\n\t\treturn e.applyStatusFilter(comp, filter)\n\tcase \"priority\":\n\t\treturn e.applyPriorityFilter(comp, filter)\n\tcase \"type\":\n\t\treturn e.applyTypeFilter(comp, filter)\n\tcase \"assignee\":\n\t\treturn e.applyAssigneeFilter(comp, filter)\n\tcase \"owner\":\n\t\treturn e.applyOwnerFilter(comp, filter)\n\tcase \"label\", \"labels\":\n\t\treturn e.applyLabelFilter(comp, filter)","sourceCodeStart":134,"sourceCodeEnd":170,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/query/evaluator.go#L134-L170","documentation":"buildFilter encountered an AST node type it has no case for; only ComparisonNode, AndNode, NotNode and the optimizable OrNode are supported. This is an internal invariant violation — it means the parser produced (or a caller constructed) a node kind the filter builder doesn't understand, and the %T verb names the offending type.","triggerScenarios":"Passing an AST containing node types outside the filter-supported subset (e.g. grouping/nesting node kinds, custom Node implementations) into Evaluate/buildFilter; a parser bug emitting a new node type after a grammar change.","commonSituations":"Constructing query ASTs programmatically with unsupported node types; version skew where a newer parser emits nodes an older evaluator doesn't handle; third-party code implementing the Node interface.","solutions":["Read the %T type in the message and remove or rewrite that node kind before calling Evaluate","Restrict programmatically built ASTs to ComparisonNode, AndNode, NotNode and label-only OrNodes","Update the evaluator/parser together — ensure both sides come from the same library version","If you need the construct, extend buildFilter with a case rather than passing it through"],"exampleFix":"// before\nnode := &CustomGroupNode{Children: children}\nevaluator.Evaluate(node)\n// after\nnode := &AndNode{Left: left, Right: right} // supported node type\nevaluator.Evaluate(node)","handlingStrategy":"type-guard","validationCode":"// Ensure the AST contains only node kinds buildFilter understands\nfunc filterSafe(n query.Node) bool {\n    switch t := n.(type) {\n    case *query.ComparisonNode, *query.AndNode, *query.NotNode:\n        return true\n    case *query.OrNode:\n        return filterSafe(t.Left) && filterSafe(t.Right)\n    default:\n        return false\n    }\n}","typeGuard":"func isFilterNode(n query.Node) bool {\n    switch n.(type) {\n    case *query.ComparisonNode, *query.AndNode, *query.NotNode, *query.OrNode:\n        return true\n    default:\n        return false\n    }\n}","tryCatchPattern":"if err := evaluator.Evaluate(ast); err != nil {\n    if strings.Contains(err.Error(), \"unexpected node type\") {\n        return fmt.Errorf(\"internal: query AST contains unsupported node: %w\", err)\n    }\n    return err\n}","preventionTips":["Build filter ASTs only with the exported node constructors of the same library version","Keep parser and evaluator versions in lockstep; never mix across versions","Pre-validate ASTs with a node-type whitelist before calling Evaluate","When adding new node types, update buildFilter's switch in the same change"],"tags":["query","ast","unsupported-node","internal"],"backgroundTag":"unsupported-query-operator","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}