gastownhall/beads · error · storage.ErrValidation

%w: %w

Error message

%w: %w

What it means

This error wraps storage.ErrValidation together with the underlying ValidateMetadataKey error, thrown by ApplyMetadataPatch when a key in the metadata patch.Set map fails key validation. It signals that the update request carries a metadata key that violates the storage layer's key rules (e.g. empty, too long, or invalid characters), so the whole update is rejected before touching the database.

Source

Thrown at internal/storage/issueops/aggregate.go:200

	if err != nil {
		return err
	}
	return AuthorizeAssigneeTransferWithPools(before, request, pools)
}

// ApplyMetadataPatch returns the canonical metadata value and whether it changes.
func ApplyMetadataPatch(current json.RawMessage, patch publicops.MetadataPatch) (json.RawMessage, bool, error) {
	if !patch.Replace.Set && !patch.Merge.Set && len(patch.Set) == 0 && len(patch.Unset) == 0 {
		return current, false, nil
	}
	setKeys := make([]string, 0, len(patch.Set))
	for key := range patch.Set {
		setKeys = append(setKeys, key)
	}
	sort.Strings(setKeys)
	for _, key := range setKeys {
		if err := storage.ValidateMetadataKey(key); err != nil {
			return nil, false, fmt.Errorf("%w: %w", storage.ErrValidation, err)
		}
	}
	for _, key := range patch.Unset {
		if err := storage.ValidateMetadataKey(key); err != nil {
			return nil, false, fmt.Errorf("%w: %w", storage.ErrValidation, err)
		}
	}
	var next json.RawMessage
	if patch.Replace.Set {
		next = append(json.RawMessage(nil), patch.Replace.Value...)
		if len(next) == 0 {
			next = json.RawMessage(`{}`)
		}
		if !json.Valid(next) {
			return nil, false, fmt.Errorf("%w: metadata replacement is not valid JSON", storage.ErrValidation)
		}
	} else {
		next = append(json.RawMessage(nil), current...)

View on GitHub (pinned to 71377f2769)

Solutions

  1. Validate each metadata key with storage.ValidateMetadataKey before constructing the patch and drop or fix invalid keys.
  2. Trim and sanitize user-supplied keys (reject empty/whitespace keys) at the CLI/API boundary.
  3. Inspect the wrapped error message to identify the exact offending key and correct it.

Example fix

// before
patch.Set[rawKey] = json.RawMessage("1") // rawKey may be empty
// after
if err := storage.ValidateMetadataKey(rawKey); err != nil {
    return fmt.Errorf("invalid metadata key %q: %w", rawKey, err)
}
patch.Set[rawKey] = json.RawMessage("1")
Defensive patterns

Strategy: validation

Validate before calling

func validSetKeys(set map[string]json.RawMessage) error {
    for k := range set {
        if err := storage.ValidateMetadataKey(k); err != nil {
            return fmt.Errorf("key %q: %w", k, err)
        }
    }
    return nil
}

Try / catch

if err := errors.Is(err, storage.ErrValidation) { /* surface invalid metadata key to caller, do not retry */ }

Prevention

When it happens

Trigger: Calling ApplyMetadataPatch (via ExecuteUpdate) with patch.Set containing at least one key for which storage.ValidateMetadataKey returns an error.

Common situations: Building metadata patches programmatically with empty-string keys; user-supplied JSON keys passed through CLI flags without sanitization; keys containing whitespace or control characters from copied config.

Related errors


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