gastownhall/beads · error
failed to marshal metadata: %w
Error message
failed to marshal metadata: %w
What it means
After applying set/unset edits, ApplyMetadataEdits marshals the resulting map back to JSON and wraps any json.Marshal error. Since every stored value is produced via MetadataEditValue (which marshals a string) or came from successfully-decoded existing JSON, this error is very rare and acts as a defensive guard against writing corrupt metadata.
Source
Thrown at internal/storage/metadata.go:310
if !ok || k == "" {
return nil, fmt.Errorf("invalid --set-metadata: expected key=value, got %q", kv)
}
if err := ValidateMetadataKey(k); err != nil {
return nil, err
}
data[k] = MetadataEditValue(v)
}
for _, k := range unsetFlags {
if err := ValidateMetadataKey(k); err != nil {
return nil, err
}
delete(data, k)
}
result, err := json.Marshal(data)
if err != nil {
return nil, fmt.Errorf("failed to marshal metadata: %w", err)
}
return json.RawMessage(result), nil
}
// MetadataEditValue converts a --set-metadata string value to JSON. Per the CLI
// contract (GH#4146), --set-metadata values are ALWAYS stored as JSON strings;
// inferring numbers/booleans/null from string content silently broke
// map[string]string round-trips for Go consumers (a numeric-looking id or a
// version like "1e3" came back as a JSON number). Typed values go through the
// explicit --metadata / --metadata-json path (MergeMetadataJSON).
func MetadataEditValue(s string) json.RawMessage {
b, _ := json.Marshal(s)
return json.RawMessage(b)
}
View on GitHub (pinned to 71377f2769)
Solutions
- Ensure any raw values you inject are valid JSON (use json.Marshal to build them).
- Reload the existing metadata from the database instead of reusing a hand-modified map.
- If CLI-only, this indicates corrupt stored metadata — reset metadata to null and re-apply edits.
- Report upstream with the offending metadata blob if reproducible from library output alone.
Example fix
// before
data["x"] = json.RawMessage(`{"oops"`) // invalid
out, err := ApplyMetadataEdits(existing, nil, nil)
// after
raw, _ := json.Marshal("value")
data["x"] = raw
out, err := ApplyMetadataEdits(existing, nil, nil) Defensive patterns
Strategy: try-catch
Validate before calling
func allValid(data map[string]json.RawMessage) bool {
for _, v := range data {
if !json.Valid(v) { return false }
}
return true
} // check the working map before/during custom edit pipelines Type guard
func validRaw(v json.RawMessage) bool { return json.Valid(v) } Try / catch
out, err := ApplyMetadataEdits(existing, set, unset)
if err != nil && strings.Contains(err.Error(), "failed to marshal metadata") {
// corrupt base: rebuild metadata from the set flags alone
out, err = ApplyMetadataEdits(nil, set, unset)
} Prevention
- Construct metadata values only via json.Marshal or MetadataEditValue.
- Treat marshal failures here as evidence of corrupt stored metadata and reload it.
- Unit-test any code path that injects raw fragments into metadata maps.
When it happens
Trigger: A json.RawMessage in the working map is not valid JSON — possible if the existing metadata map was decoded from fragments that are individually invalid, or if the map was constructed by hand and passed through other code paths; essentially never fires on CLI-supplied flags.
Common situations: Programmatic callers composing json.RawMessage values manually with malformed content; corrupted stored metadata that happened to pass the initial Unmarshal through a loose path; encoder edge cases with pathological data.
Related errors
- marshaling Linear milestone metadata: %w
- failed to marshal metadata: %w
- failed to marshal merged metadata: %w
- marshaling metadata ref %q: %w
- ExternalDoltConfig: must set Socket or (Host, Port)
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/6aafdb52ef96adc7.
Report an issue: GitHub.