{"record":{"id":"8f5bcb8d95bcca61","repo":"gastownhall/beads","slug":"re-parsing-metadata-w","errorCode":null,"errorMessage":"re-parsing metadata: %w","messagePattern":"re-parsing metadata: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/types/types.go","lineNumber":1468,"sourceCode":"\ttargetID := keyToID[target]\n\tif targetID == \"\" {\n\t\ttargetID = target\n\t}\n\tif targetID == \"\" {\n\t\treturn nil, fmt.Errorf(\"dep target %q not found\", target)\n\t}\n\treturn NewGraphEdgeDependency(issueID, targetID, depType, \"\", \"\", \"\", \"\", nil)\n}\n\n// MergeMetadataRefs merges resolved metadata_refs into an issue's existing\n// metadata JSON: each refs entry maps a metadata key to a plan-local node\n// key, which is replaced with its minted ID from keyToID. Shared by the\n// embedded and domain graph-apply paths.\nfunc MergeMetadataRefs(existing json.RawMessage, refs map[string]string, keyToID map[string]string) (json.RawMessage, error) {\n\tmerged := make(map[string]json.RawMessage, len(refs))\n\tif len(existing) > 0 {\n\t\tif err := json.Unmarshal(existing, &merged); err != nil {\n\t\t\treturn nil, fmt.Errorf(\"re-parsing metadata: %w\", err)\n\t\t}\n\t}\n\tfor metaKey, refKey := range refs {\n\t\tresolvedID, ok := keyToID[refKey]\n\t\tif !ok {\n\t\t\treturn nil, fmt.Errorf(\"metadata_ref %q references unknown key %q\", metaKey, refKey)\n\t\t}\n\t\tidJSON, err := json.Marshal(resolvedID)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"marshaling metadata ref %q: %w\", metaKey, err)\n\t\t}\n\t\tmerged[metaKey] = idJSON\n\t}\n\treturn json.Marshal(merged)\n}\n\n// ParseWaitsForGateMetadata extracts the waits-for gate type from dependency metadata.\n// Note: spawner identity comes from dependencies.depends_on_id in storage/query paths;","sourceCodeStart":1450,"sourceCodeEnd":1486,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/types/types.go#L1450-L1486","documentation":"MergeMetadataRefs merges resolved metadata_refs into an issue's existing metadata JSON. It first unmarshals the existing raw JSON into a map; if the existing blob is not a valid JSON object, the unmarshal fails and is wrapped as 're-parsing metadata'.","triggerScenarios":"Calling MergeMetadataRefs with existing metadata that is not valid JSON or is not a JSON object (e.g. a JSON array, string, or garbage bytes).","commonSituations":"Rows written by older versions or external tools with non-object metadata; corrupted metadata columns; hand-edited metadata values in the database.","solutions":["Validate/repair the existing metadata so it is a JSON object before merging","If the row is legacy/corrupt, reset its metadata to '{}' and re-apply mergeMetadataRefs","Check json.Valid(existing) as a guard before calling MergeMetadataRefs"],"exampleFix":"// before\nmerged, err := MergeMetadataRefs(corruptRaw, refs, keyToID) // fails\n// after\nif !json.Valid(corruptRaw) { corruptRaw = json.RawMessage(\"{}\") }\nmerged, err := MergeMetadataRefs(corruptRaw, refs, keyToID)","handlingStrategy":"validation","validationCode":"func canMergeMetadata(existing json.RawMessage) bool {\n\tif len(existing) == 0 { return true }\n\tvar m map[string]json.RawMessage\n\treturn json.Unmarshal(existing, &m) == nil\n}","typeGuard":"func isJSONObject(raw json.RawMessage) bool {\n\tvar m map[string]json.RawMessage\n\treturn len(raw) > 0 && json.Unmarshal(raw, &m) == nil\n}","tryCatchPattern":"merged, err := types.MergeMetadataRefs(existing, refs, keyToID)\nif err != nil {\n\tif strings.Contains(err.Error(), \"re-parsing metadata\") {\n\t\t// fall back: start from an empty object\n\t\tmerged, err = types.MergeMetadataRefs(json.RawMessage(\"{}\"), refs, keyToID)\n\t}\n\tif err != nil { return err }\n}","preventionTips":["Always store metadata as a JSON object","Use json.Valid / object-shape checks before persisting external metadata","Reset corrupted metadata rows to '{}' before merging"],"tags":["go","json","metadata","unmarshal"],"backgroundTag":"invalid-json-metadata","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}