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
- Run json.Valid (or json.Marshal the value) before assigning Replace.Value
- Use json.Marshal on a Go map/struct to guarantee valid JSON
- Fix syntax errors in hand-written JSON (quotes, commas, trailing tokens)
- 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
- Always produce Replace.Value with json.Marshal, never string concatenation
- Run json.Valid on any hand-written or templated JSON before use
- Avoid fmt.Sprintf("%v", map) — Go map formatting is not JSON
- Store metadata templates as valid JSON files, not inline strings
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
- metadata value for key %q is not valid JSON
- ExternalDoltConfig: must set Socket or (Host, Port)
- %w: metadata replacement is not valid JSON
- %w: metadata merge must be a JSON object
- %w: metadata merge: %v
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/fb9c629ebcc78689.
Report an issue: GitHub.