gastownhall/beads · error

metadata fields only support = operator

Error message

metadata fields only support = operator

What it means

The beads query engine's metadata.<key> fields (GH#1406) are only implemented for exact equality matching. applyMetadataFilter runs in filter mode (AND-style filters pushed to IssueFilter.MetadataFields); any other comparison operator (!=, =~, >, IN, etc.) is rejected with this error. It exists because metadata filtering maps to a storage-level field filter that has no semantics for other operators.

Source

Thrown at internal/query/evaluator.go:502

	return nil
}

func (e *Evaluator) applyMolTypeFilter(comp *ComparisonNode, filter *types.IssueFilter) error {
	if comp.Op != OpEquals {
		return fmt.Errorf("mol_type only supports = operator")
	}
	mt := types.MolType(strings.ToLower(comp.Value))
	if !mt.IsValid() {
		return fmt.Errorf("invalid mol_type: %s", comp.Value)
	}
	filter.MolType = &mt
	return nil
}

// applyMetadataFilter handles metadata.<key>=<value> queries (GH#1406).
func (e *Evaluator) applyMetadataFilter(comp *ComparisonNode, filter *types.IssueFilter) error {
	if comp.Op != OpEquals {
		return fmt.Errorf("metadata fields only support = operator")
	}
	key := strings.TrimPrefix(comp.Field, "metadata.")
	if err := storage.ValidateMetadataKey(key); err != nil {
		return err
	}
	if filter.MetadataFields == nil {
		filter.MetadataFields = make(map[string]string)
	}
	filter.MetadataFields[key] = comp.Value
	return nil
}

// applyHasMetadataKeyFilter handles has_metadata_key=<keyname> queries (GH#1406).
func (e *Evaluator) applyHasMetadataKeyFilter(comp *ComparisonNode, filter *types.IssueFilter) error {
	if comp.Op != OpEquals {
		return fmt.Errorf("has_metadata_key only supports = operator")
	}
	if err := storage.ValidateMetadataKey(comp.Value); err != nil {

View on GitHub (pinned to 71377f2769)

Solutions

  1. Rewrite the comparison to use the = operator, e.g. `metadata.team=core` instead of `metadata.team!=core`
  2. If you need negation, invert the logic: fetch with metadata.team=core and exclude those results, or use NOT (NOT is also limited to = in filter mode)
  3. If you need richer matching, filter in a post-processing step over the JSON metadata of returned issues
  4. Validate metadata keys are non-empty with storage.ValidateMetadataKey semantics; a malformed key yields a different error, but keep the query shape `metadata.<key>=<scalar>`

Example fix

// before
bd list 'metadata.team!=core'
// after
bd list 'metadata.team=core'  # only = is supported for metadata fields
Defensive patterns

Strategy: validation

Validate before calling

// validate metadata query shape before running
var metadataEq = regexp.MustCompile(`^metadata\.[A-Za-z0-9_-]+\s*=\s*\S+$`)
if !metadataEq.MatchString(clause) {
    return fmt.Errorf("metadata filter must be metadata.<key>=<value>")
}

Type guard

func isMetadataEquals(field, op string) bool {
    return strings.HasPrefix(field, "metadata.") && op == "="
}

Prevention

When it happens

Trigger: Running a bd query like `metadata.team!=core`, `metadata.team=~cor`, or `metadata.priority>2` — any metadata.<key> comparison whose Op is not OpEquals, dispatched via applyComparison to applyMetadataFilter.

Common situations: Users assume metadata fields behave like regular fields and try negation, regex, or range matching; queries written by analogy with type/status fields which do support more operators; OR-query authors hit the sibling error at buildMetadataPredicate (index 2142) instead.

Related errors


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