gastownhall/beads · error

notes only supports = operator

Error message

notes only supports = operator

What it means

applyNotesFilter only permits the = operator on the notes field, implemented as a substring match via filter.NotesContains. Any other operator is rejected. Unlike assignee/label/description, notes has no special 'none' handling — the raw value (even empty) becomes NotesContains.

Source

Thrown at internal/query/evaluator.go:326

	filter.TitleContains = comp.Value
	return nil
}

func (e *Evaluator) applyDescriptionFilter(comp *ComparisonNode, filter *types.IssueFilter) error {
	if comp.Op != OpEquals {
		return fmt.Errorf("description only supports = operator (use desc contains pattern)")
	}
	if comp.Value == "" || strings.ToLower(comp.Value) == "none" || strings.ToLower(comp.Value) == "null" {
		filter.EmptyDescription = true
	} else {
		filter.DescriptionContains = comp.Value
	}
	return nil
}

func (e *Evaluator) applyNotesFilter(comp *ComparisonNode, filter *types.IssueFilter) error {
	if comp.Op != OpEquals {
		return fmt.Errorf("notes only supports = operator")
	}
	filter.NotesContains = comp.Value
	return nil
}

func (e *Evaluator) applyCreatedFilter(comp *ComparisonNode, filter *types.IssueFilter) error {
	t, err := e.parseTimeValue(comp)
	if err != nil {
		return fmt.Errorf("invalid created time: %w", err)
	}
	switch comp.Op {
	case OpEquals:
		// For equals, set both before and after to bracket the day
		dayStart := time.Date(t.Year(), t.Month(), t.Day(), 0, 0, 0, 0, t.Location())
		dayEnd := dayStart.Add(24 * time.Hour)
		filter.CreatedAfter = &dayStart
		filter.CreatedBefore = &dayEnd
	case OpGreater:

View on GitHub (pinned to 71377f2769)

Solutions

  1. Use `notes = <substring>` in filter mode (substring match)
  2. Use predicate mode where notes contains / other operators are supported via buildNotesPredicate
  3. Exclude notes matches by post-filtering results in application code

Example fix

// before
bd list 'notes contains escalated'
// after
bd list 'notes = escalated'
Defensive patterns

Strategy: validation

Validate before calling

// Validate notes comparisons before evaluation
if comp.Field == "notes" && comp.Op != OpEquals {
	return fmt.Errorf("use 'notes = <substring>' in filter mode")
}

Type guard

func notesOpSupported(op Op) bool { return op == OpEquals }

Try / catch

if err := eval.applyComparison(comp, filter); err != nil {
	if strings.Contains(err.Error(), "notes only supports = operator") {
		// rewrite to notes = <substring> or use predicate mode
	}
}

Prevention

When it happens

Trigger: Queries like `notes != x`, `notes contains foo`, or `notes ~ y` reach applyNotesFilter with comp.Op != OpEquals and return this error.

Common situations: Users searching agent/audit notes with contains syntax; attempts to exclude issues mentioning a string via !=; queries modeled on the predicate-mode notes API.

Related errors


AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30). Data as JSON: /api/errors/51877394aa4199f7. Report an issue: GitHub.