gastownhall/beads · error · storage.ErrValidation
%w: metadata value for key %q is not valid JSON
Error message
%w: metadata value for key %q is not valid JSON
What it means
ApplyMetadataPatch wraps storage.ErrValidation when a value in patch.Set for a given key is not valid JSON. Metadata values are stored as raw JSON, so every set value must parse; the offending key is named in the message.
Source
Thrown at internal/storage/issueops/aggregate.go:241
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" {
if err := json.Unmarshal(next, &values); err != nil {
return nil, false, fmt.Errorf("%w: metadata edits require an object: %v", storage.ErrValidation, err)
}
}
for _, key := range setKeys {
value := patch.Set[key]
if !json.Valid(value) {
return nil, false, fmt.Errorf("%w: metadata value for key %q is not valid JSON", storage.ErrValidation, key)
}
values[key] = append(json.RawMessage(nil), value...)
}
for _, key := range patch.Unset {
delete(values, key)
}
encoded, err := json.Marshal(values)
if err != nil {
return nil, false, fmt.Errorf("%w: encode metadata edits: %v", storage.ErrValidation, err)
}
next = encoded
}
}
if err := ValidateMetadataIfConfigured(next); err != nil {
return nil, false, err
}
changed, err := metadataChanged(current, next)
if err != nil {View on GitHub (pinned to 71377f2769)
Solutions
- Marshal values with encoding/json (json.Marshal / json.Marshaler) instead of casting raw strings to json.RawMessage.
- Pre-validate each value with json.Valid in the caller before building the patch.
- For string values, use json.Marshal(string) to get proper quoting.
Example fix
// before
patch.Set["name"] = json.RawMessage(`Alice`) // invalid JSON
// after
b, _ := json.Marshal("Alice")
patch.Set["name"] = b // "Alice" (quoted, valid JSON) Defensive patterns
Strategy: validation
Validate before calling
func validSetValues(set map[string]json.RawMessage) error {
for k, v := range set {
if !json.Valid(v) {
return fmt.Errorf("key %q: invalid JSON value", k)
}
}
return nil
} Try / catch
if errors.Is(err, storage.ErrValidation) && strings.Contains(err.Error(), "not valid JSON") { /* fix the named key's value, no retry */ } Prevention
- Always produce values via json.Marshal rather than casting raw strings to json.RawMessage.
- Run json.Valid on each value before building the patch.
- Quote string values properly when hand-authoring JSON in CLI flags.
- Add tests that round-trip all metadata value types through the patch.
When it happens
Trigger: Calling ApplyMetadataPatch with patch.Set[key] set to non-JSON bytes, e.g. plain strings without quotes ("hello" instead of "\"hello\""), trailing commas, or partially marshaled values.
Common situations: Passing CLI flag values directly as raw JSON without quoting strings; hand-concatenating JSON; converting user text to json.RawMessage instead of json.Marshal; truncated values from config pipelines.
Related errors
- 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
- %w: metadata edits require an object: %v
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/8a56a2820f6e909e.
Report an issue: GitHub.