gastownhall/beads · error

metadata replacement is not valid JSON

Error message

metadata replacement is not valid JSON

What it means

When Replace.Set is true and a non-empty replacement value is supplied, validateMetadataPatch requires it to be valid JSON via json.Valid; otherwise this error is returned. A replacement must be a parseable JSON document.

Source

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

	if err := validateMetadataPatch(request.Patch.Metadata); err != nil {
		return validationError(err)
	}
	return nil
}

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

func validateMetadataPatch(metadata publicops.MetadataPatch) error {
	if metadata.Replace.Set && (metadata.Merge.Set || len(metadata.Set) > 0 || len(metadata.Unset) > 0) {
		return fmt.Errorf("metadata replacement cannot combine with incremental edits")
	}
	if metadata.Replace.Set && len(metadata.Replace.Value) > 0 && !json.Valid(metadata.Replace.Value) {
		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)

View on GitHub (pinned to 71377f2769)

Solutions

  1. Run json.Valid (or json.Marshal the value) before assigning Replace.Value
  2. Use json.Marshal on a Go map/struct to guarantee valid JSON
  3. Fix syntax errors in hand-written JSON (quotes, commas, trailing tokens)
  4. Check errors.Is(err, publicops.ErrValidation) in callers to surface input problems

Example fix

// before
patch.Metadata.Replace.Value = []byte(`{priority: high}`)
// after
v, _ := json.Marshal(map[string]any{"priority": "high"})
patch.Metadata.Replace.Set = true
patch.Metadata.Replace.Value = v
Defensive patterns

Strategy: validation

Validate before calling

func validReplacement(v []byte) error {
    if len(v) > 0 && !json.Valid(v) { return fmt.Errorf("replacement is not valid JSON") }
    return nil
}

Try / catch

if err := uc.Update(ctx, req); err != nil {
    if errors.Is(err, publicops.ErrValidation) && strings.Contains(err.Error(), "valid JSON") {
        // re-marshal the replacement value and retry once
    }
    return err
}

Prevention

When it happens

Trigger: Calling Update with Patch.Metadata.Replace.Set = true and Replace.Value = []byte that json.Valid rejects (truncated, single quotes, trailing comma, raw Go map formatting, etc.).

Common situations: Hand-writing metadata in a CLI flag or YAML/env-injected string, string-concatenated JSON, or []byte(fmt.Sprintf("%v", map)) output that is not valid JSON.

Related errors


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