gastownhall/beads · error · storage.ErrValidation
%w: metadata replacement is not valid JSON
Error message
%w: metadata replacement is not valid JSON
What it means
ApplyMetadataPatch throws this wrapped storage.ErrValidation when patch.Replace.Set is true but the replacement metadata document is not valid JSON. The library requires the entire metadata blob to always be parseable JSON, so it rejects the update before writing.
Source
Thrown at internal/storage/issueops/aggregate.go:215
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...)
if patch.Merge.Set {
// A JSON null unmarshals into a nil overlay map, so the merge
// below would silently accept it as "change nothing".
if strings.TrimSpace(string(patch.Merge.Value)) == "null" {
return nil, false, fmt.Errorf("%w: metadata merge must be a JSON object", storage.ErrValidation)
}
merged, err := storage.MergeMetadataJSON(next, patch.Merge.Value)
if err != nil {
return nil, false, fmt.Errorf("%w: metadata merge: %v", storage.ErrValidation, err)
}
next = merged
}
if len(patch.Set) > 0 || len(patch.Unset) > 0 {
values := make(map[string]json.RawMessage)
if len(next) > 0 && string(next) != "null" {View on GitHub (pinned to 71377f2769)
Solutions
- Run json.Valid on the replacement document in the caller before building the patch.
- Marshal a Go map/struct with encoding/json instead of hand-building JSON strings.
- Fix shell quoting or file content so the value is a complete, valid JSON object.
Example fix
// before
req.MetadataReplace = []byte(`{"a": 1`) // truncated
// after
v := map[string]any{"a": 1}
b, err := json.Marshal(v)
if err != nil {
return err
}
req.MetadataReplace = b // guaranteed valid JSON Defensive patterns
Strategy: validation
Validate before calling
func validReplace(v json.RawMessage) bool {
t := strings.TrimSpace(string(v))
return t == "" || json.Valid(v)
} Try / catch
if errors.Is(err, storage.ErrValidation) && strings.Contains(err.Error(), "not valid JSON") { /* fix replacement document, no retry */ } Prevention
- Produce replacement metadata with json.Marshal, never string concatenation.
- Check json.Valid on the replacement before setting patch.Replace.
- Beware shell quoting: prefer --metadata-from-file or stdin for complex JSON.
- Treat empty replacement as {} deliberately, not accidentally.
When it happens
Trigger: Calling ApplyMetadataPatch with patch.Replace.Set=true and patch.Replace.Value being invalid JSON (e.g. raw text, truncated JSON, or a value produced by naive string concatenation).
Common situations: Shell quoting mangling `--metadata '{"a":1'`; templates that interpolate into JSON strings; reading a corrupted metadata blob from a config file; passing an empty value (which the code silently coerces to `{}`) followed by non-JSON text.
Related errors
- ExternalDoltConfig: must set Socket or (Host, Port)
- %w: metadata merge must be a JSON object
- %w: metadata merge: %v
- %w: metadata edits require an object: %v
- %w: metadata value for key %q is not valid JSON
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/aa42627feb4535c4.
Report an issue: GitHub.