gastownhall/beads · error

invalid metadata key filter: %w

Error message

invalid metadata key filter: %w

What it means

ValidateMetadataFilters rejects the --has-key (hasKey) argument when storage.ValidateMetadataKey deems it malformed. The library validates metadata keys up front so a bad key fails fast instead of producing a filter that can never match. The wrapped inner error from ValidateMetadataKey names the specific rule violated.

Source

Thrown at internal/workapi/metadata.go:28

// ValidateMetadataFilters refuses a metadata key the query layer cannot spell.
//
// Both builders call it, so the leaf contract's "keys are validated inside" is
// true of every Reader implementation rather than of the CLI only. Before it
// existed the sole check lived in the SQL builder, whose error surfaced wrapped
// in the storage method's name — a shape nothing above storage can classify —
// so a typo'd key reached `bd list` as a usage error and the HTTP surface as a
// 500, on a parameter the frozen document promises a 400 for.
//
// The CLI still validates the same input at flag-parse time and still reports
// it in its own words: this is the floor under every caller, not a replacement
// for a front door's usage error.
//
// Keys are checked in sorted order so a request with two bad keys always names
// the same one.
func ValidateMetadataFilters(fields map[string]string, hasKey string) error {
	if hasKey != "" {
		if err := storage.ValidateMetadataKey(hasKey); err != nil {
			return fmt.Errorf("invalid metadata key filter: %w", err)
		}
	}
	keys := make([]string, 0, len(fields))
	for k := range fields {
		keys = append(keys, k)
	}
	sort.Strings(keys)
	for _, k := range keys {
		if err := storage.ValidateMetadataKey(k); err != nil {
			return fmt.Errorf("invalid metadata field key: %w", err)
		}
	}
	return nil
}

View on GitHub (pinned to 71377f2769)

Solutions

  1. Read the wrapped ValidateMetadataKey error to see which rule the key breaks
  2. Normalize the key: trim whitespace, remove disallowed characters, match the allowed pattern/length
  3. If the intent is to filter on a field, verify the exact key with `bd show` on a known issue carrying that metadata

Example fix

// before
bd list --has-key "review status"
// after
bd list --has-key "review-status"
Defensive patterns

Strategy: validation

Validate before calling

if hasKey != "" {
	if err := storage.ValidateMetadataKey(hasKey); err != nil {
		return fmt.Errorf("--has-key: %w", err)
	}
}

Try / catch

err := ValidateMetadataFilters(fields, hasKey)
if err != nil && strings.HasPrefix(err.Error(), "invalid metadata key filter") {
	return fmt.Errorf("check --has-key value: %w", err)
}

Prevention

When it happens

Trigger: Calling BuildListFilter or BuildReadyFilter with fields/hasKey where hasKey fails ValidateMetadataKey — e.g. an empty-or-whitespace key, a key with illegal characters, or one exceeding the length cap.

Common situations: Scripting `bd list --has-key` with a value containing spaces or dots; passing a JSON-style dotted path when only flat alphanumeric keys are allowed; copy-pasting a label that includes trailing whitespace.

Related errors


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