{"record":{"id":"32555546f108ad66","repo":"gastownhall/beads","slug":"title-only-supports-operator-use-title-contains","errorCode":null,"errorMessage":"title only supports = operator (use title contains pattern)","messagePattern":"title only supports = operator \\(use title contains pattern\\)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/query/evaluator.go","lineNumber":306,"sourceCode":"\t// Owner filtering requires predicate\n\treturn fmt.Errorf(\"owner filtering requires predicate mode\")\n}\n\nfunc (e *Evaluator) applyLabelFilter(comp *ComparisonNode, filter *types.IssueFilter) error {\n\tif comp.Op != OpEquals {\n\t\treturn fmt.Errorf(\"label only supports = operator\")\n\t}\n\tif comp.Value == \"\" || strings.ToLower(comp.Value) == \"none\" || strings.ToLower(comp.Value) == \"null\" {\n\t\tfilter.NoLabels = true\n\t} else {\n\t\tfilter.Labels = append(filter.Labels, comp.Value)\n\t}\n\treturn nil\n}\n\nfunc (e *Evaluator) applyTitleFilter(comp *ComparisonNode, filter *types.IssueFilter) error {\n\tif comp.Op != OpEquals {\n\t\treturn fmt.Errorf(\"title only supports = operator (use title contains pattern)\")\n\t}\n\tfilter.TitleContains = comp.Value\n\treturn nil\n}\n\nfunc (e *Evaluator) applyDescriptionFilter(comp *ComparisonNode, filter *types.IssueFilter) error {\n\tif comp.Op != OpEquals {\n\t\treturn fmt.Errorf(\"description only supports = operator (use desc contains pattern)\")\n\t}\n\tif comp.Value == \"\" || strings.ToLower(comp.Value) == \"none\" || strings.ToLower(comp.Value) == \"null\" {\n\t\tfilter.EmptyDescription = true\n\t} else {\n\t\tfilter.DescriptionContains = comp.Value\n\t}\n\treturn nil\n}\n\nfunc (e *Evaluator) applyNotesFilter(comp *ComparisonNode, filter *types.IssueFilter) error {","sourceCodeStart":288,"sourceCodeEnd":324,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/query/evaluator.go#L288-L324","documentation":"applyTitleFilter supports only the = operator; the match is implemented as a substring check via filter.TitleContains. Any other operator on title is rejected. The error message itself points to the intended alternative: `title contains pattern` style queries belong in predicate mode.","triggerScenarios":"Queries like `title contains foo`, `title != bar`, or `title ~ x` hit applyTitleFilter with comp.Op != OpEquals and fail. Only bare `title = something` succeeds (and behaves as a contains match).","commonSituations":"Users writing natural-language-ish searches (`title contains login bug`) that the filter-mode evaluator does not implement; regex-style title matching assumptions; queries ported from other issue trackers.","solutions":["Use `title = <substring>` which sets TitleContains (substring match) in filter mode","Switch to predicate mode where title contains/other operators are supported via buildTitlePredicate","Pre-filter by other fields and apply title matching in application code"],"exampleFix":"// before\nbd list 'title contains flaky'\n// after\nbd list 'title = flaky'","handlingStrategy":"validation","validationCode":"// Validate title comparisons before evaluation\nif comp.Field == \"title\" && comp.Op != OpEquals {\n\treturn fmt.Errorf(\"use 'title = <substring>' in filter mode\")\n}","typeGuard":"func titleOpSupported(op Op) bool { return op == OpEquals }","tryCatchPattern":"if err := eval.applyComparison(comp, filter); err != nil {\n\tvar _ *UnsupportedOpError\n\tif strings.Contains(err.Error(), \"title only supports = operator\") {\n\t\t// rewrite to title = <substring> or switch to predicate mode\n\t}\n}","preventionTips":["Use `title = <substring>` (it matches as contains in filter mode)","Use predicate mode for contains/other title operators","Pre-normalize user-supplied queries that use contains on title"],"tags":["query","filter","title","unsupported-operator"],"backgroundTag":"unsupported-query-operator","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}