{"record":{"id":"d9a56234b0ade5c7","repo":"gastownhall/beads","slug":"failed-to-marshal-merged-metadata-w","errorCode":null,"errorMessage":"failed to marshal merged metadata: %w","messagePattern":"failed to marshal merged metadata: %w","errorType":"console","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/metadata.go","lineNumber":271,"sourceCode":"\t\tif trimmed != \"\" && trimmed != \"null\" {\n\t\t\tif err := json.Unmarshal(existing, &base); err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"existing metadata is not a JSON object: %w\", err)\n\t\t\t}\n\t\t}\n\t}\n\n\toverlay := make(map[string]json.RawMessage)\n\tif err := json.Unmarshal(incoming, &overlay); err != nil {\n\t\treturn nil, fmt.Errorf(\"new metadata is not a JSON object: %w\", err)\n\t}\n\n\tfor k, v := range overlay {\n\t\tbase[k] = v\n\t}\n\n\tresult, err := json.Marshal(base)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to marshal merged metadata: %w\", err)\n\t}\n\treturn json.RawMessage(result), nil\n}\n\n// ApplyMetadataEdits applies incremental set (key=value) and unset (key) edits\n// to existing metadata and returns the merged JSON. Set values are typed via\n// MetadataEditValue; keys are validated with ValidateMetadataKey.\nfunc ApplyMetadataEdits(existing json.RawMessage, setFlags, unsetFlags []string) (json.RawMessage, error) {\n\tdata := make(map[string]json.RawMessage)\n\tif len(existing) > 0 {\n\t\ttrimmed := strings.TrimSpace(string(existing))\n\t\tif trimmed != \"\" && trimmed != \"null\" {\n\t\t\tif err := json.Unmarshal(existing, &data); err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"existing metadata is not a JSON object: %w\", err)\n\t\t\t}\n\t\t}\n\t}\n","sourceCodeStart":253,"sourceCodeEnd":289,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/metadata.go#L253-L289","documentation":"After merging, MergeMetadataJSON marshals the combined map[string]json.RawMessage back to JSON; this error wraps any json.Marshal failure. It is rare because the map was just populated by successfully-unmarshaled JSON; it mainly fires when a stored value inside the map is itself invalid raw JSON. The library throws it as a safety net rather than writing corrupt metadata.","triggerScenarios":"The merged base map contains a json.RawMessage value that is not valid JSON (possible when existing was decoded loosely or constructed manually and then re-fed), or an out-of-memory/unsupported-value edge during Marshal.","commonSituations":"Re-merging output that was produced by an external tool and partially rewritten; programmatically composing json.RawMessage values by hand with malformed fragments; extremely deep nesting hitting encoder limits.","solutions":["Validate every raw value fragment is itself valid JSON (json.Valid) before composing the map.","Re-obtain the existing metadata from storage instead of reusing a mutated copy.","If programmatically building values, produce them with json.Marshal, never string concatenation.","Report upstream if it reproduces on data produced solely by MergeMetadataJSON — that indicates corrupt stored metadata."],"exampleFix":"// before\nbase[\"cfg\"] = json.RawMessage(`{broken`) // invalid fragment\nmerged, err := MergeMetadataJSON(existing, incoming)\n// after\nv, _ := json.Marshal(map[string]string{\"a\": \"b\"})\nbase[\"cfg\"] = v\nmerged, err := MergeMetadataJSON(existing, incoming)","handlingStrategy":"try-catch","validationCode":"for k, v := range fragments {\n    if !json.Valid(v) {\n        return fmt.Errorf(\"fragment %q is not valid JSON\", k)\n    }\n} // run on any hand-built json.RawMessage map before merging","typeGuard":"func validRaw(v json.RawMessage) bool { return json.Valid(v) }","tryCatchPattern":"result, err := MergeMetadataJSON(existing, incoming)\nif err != nil && strings.Contains(err.Error(), \"failed to marshal merged metadata\") {\n    // stored data is corrupt: start fresh from incoming only\n    result, err = MergeMetadataJSON(nil, incoming)\n}","preventionTips":["Never build json.RawMessage by string concatenation; always json.Marshal.","Treat this error as a corruption signal: reload the original metadata from storage.","Add json.Valid assertions in tests for any code composing metadata fragments."],"tags":["json","metadata","serialization"],"backgroundTag":"metadata-serialization-failed","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}