gastownhall/beads · error

metadata value for key %q is not valid JSON

Error message

metadata value for key %q is not valid JSON

What it means

Each key in the Set map must have a value that is valid JSON (json.Valid). This per-key check rejects individual bad values and names the offending key, after ValidateMetadataKey approves the key itself.

Source

Thrown at internal/storage/uow/issue_operations.go:568

		return fmt.Errorf("metadata replacement is not valid JSON")
	}
	if metadata.Merge.Set {
		var object map[string]json.RawMessage
		if len(metadata.Merge.Value) == 0 || json.Unmarshal(metadata.Merge.Value, &object) != nil || object == nil {
			return fmt.Errorf("metadata merge must be a JSON object")
		}
	}
	keys := make([]string, 0, len(metadata.Set))
	for key := range metadata.Set {
		keys = append(keys, key)
	}
	sort.Strings(keys)
	for _, key := range keys {
		if err := storage.ValidateMetadataKey(key); err != nil {
			return err
		}
		if !json.Valid(metadata.Set[key]) {
			return fmt.Errorf("metadata value for key %q is not valid JSON", key)
		}
	}
	for _, key := range metadata.Unset {
		if err := storage.ValidateMetadataKey(key); err != nil {
			return err
		}
	}
	return nil
}

func validateReopenRequest(request publicops.ReopenRequest) error {
	if request.Actor == "" || request.IssueID == "" {
		return validationError(fmt.Errorf("reopen: actor and issue ID must not be empty"))
	}
	return nil
}

func semanticIssueEqual(left, right *types.Issue) bool {

View on GitHub (pinned to 71377f2769)

Solutions

  1. Wrap non-JSON scalar values in quotes or marshal them with json.Marshal
  2. Validate each value with json.Valid before building the patch
  3. Use a helper that converts Go values via json.Marshal rather than raw bytes
  4. Quote string values in shell: --set priority='"high"'

Example fix

// before
patch.Metadata.Set["priority"] = []byte(`high`)
// after
v, _ := json.Marshal("high")
patch.Metadata.Set["priority"] = v
Defensive patterns

Strategy: validation

Validate before calling

func validSetValues(set map[string]json.RawMessage) error {
    for k, v := range set {
        if err := storage.ValidateMetadataKey(k); err != nil { return err }
        if !json.Valid(v) { return fmt.Errorf("value for %q is not valid JSON", k) }
    }
    return nil
}

Try / catch

if err := uc.Update(ctx, req); err != nil {
    if errors.Is(err, publicops.ErrValidation) && strings.Contains(err.Error(), "valid JSON") {
        // extract the offending key from the message, fix its value, retry
    }
    return err
}

Prevention

When it happens

Trigger: Calling Update with Patch.Metadata.Set containing an entry whose json.RawMessage / []byte value is not parseable JSON — e.g. []byte(`high`) instead of []byte(`"high"`), or an empty value.

Common situations: Users passing raw strings via CLI --set flags without quoting, values built with fmt.Sprintf instead of json.Marshal, or empty strings from unset environment variables.

Related errors


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