gastownhall/beads · error

NOT only supports simple comparisons in filter mode

Error message

NOT only supports simple comparisons in filter mode

What it means

In filter mode, NOT is translated into IssueFilter exclusion lists (ExcludeStatus, ExcludeTypes), which only exist for simple equality comparisons. applyNot type-asserts not.Operand to *ComparisonNode; if the operand is any other node (AND/OR group, nested NOT, etc.) the assertion fails and this error is returned. Nested or compound NOT expressions require predicate mode.

Source

Thrown at internal/query/evaluator.go:586

	}
	return func(i *types.Issue) bool {
		if len(i.Metadata) == 0 {
			return false
		}
		var data map[string]json.RawMessage
		if err := json.Unmarshal(i.Metadata, &data); err != nil {
			return false
		}
		_, ok := data[key]
		return ok
	}, nil
}

// applyNot applies a NOT expression to the filter.
func (e *Evaluator) applyNot(not *NotNode, filter *types.IssueFilter) error {
	comp, ok := not.Operand.(*ComparisonNode)
	if !ok {
		return fmt.Errorf("NOT only supports simple comparisons in filter mode")
	}

	switch comp.Field {
	case "status":
		if comp.Op != OpEquals {
			return fmt.Errorf("NOT status only supports = operator")
		}
		status := types.Status(strings.ToLower(comp.Value))
		filter.ExcludeStatus = append(filter.ExcludeStatus, status)
		return nil
	case "type":
		if comp.Op != OpEquals {
			return fmt.Errorf("NOT type only supports = operator")
		}
		issueType := types.IssueType(strings.ToLower(comp.Value))
		filter.ExcludeTypes = append(filter.ExcludeTypes, issueType)
		return nil
	default:

View on GitHub (pinned to 71377f2769)

Solutions

  1. Rewrite the query so NOT wraps a single field comparison, e.g. `NOT status=open`
  2. Push the negation inward manually: `NOT (a OR b)` is generally not expressible; enumerate the exclusions instead, e.g. `NOT status=open NOT status=closed`
  3. Avoid nested NOT; `NOT NOT x` is not supported in filter mode
  4. If you need compound negation, run separate queries and combine results in a script

Example fix

// before
bd list 'NOT (status=open OR priority=1)'
// after
bd list 'NOT status=open NOT priority=1'  # simple comparisons only in filter mode
Defensive patterns

Strategy: validation

Validate before calling

// NOT must wrap a single field=value comparison in filter mode
func notClauseValid(operandField, operandOp string) bool {
    switch operandField {
    case "status", "type":
        return operandOp == "="
    default:
        return false
    }
}

Type guard

func isSimpleComparison(n Node) (*ComparisonNode, bool) {
    comp, ok := n.(*ComparisonNode)
    return comp, ok
}

Try / catch

result, err := eval.Evaluate(expr)
if err != nil {
    var nodeErr *NodeError
    if strings.Contains(err.Error(), "NOT only supports simple comparisons") {
        // rewrite query without compound NOT or fall back to client-side filtering
    }
    return result, err
}

Prevention

When it happens

Trigger: Queries like `NOT (status=open OR priority=1)` or `NOT NOT status=open` where the NOT operand is not a single ComparisonNode, called from buildFilter or extractBaseFilters.

Common situations: Writing boolean-algebra style queries assuming full NOT generality; wrapping a group in NOT to 'invert' a complex clause; tools generating queries programmatically emitting NOT around compound expressions.

Related errors


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