{"record":{"id":"8de3ddb15571bbff","repo":"gastownhall/beads","slug":"invalid-s-w","errorCode":null,"errorMessage":"invalid %s: %w","messagePattern":"invalid (.+?): %w","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/issueops/update.go","lineNumber":887,"sourceCode":"// into a concrete \"metadata\" value on resolved, using oldIssue.Metadata (read in\n// the same mutation transaction) as the base. It is a no-op when no metadata\n// operation keys are present.\nfunc resolveMetadataMergeOps(oldIssue *types.Issue, updates, resolved map[string]interface{}) error {\n\t_, hasMerge := updates[OpMergeMetadata]\n\t_, hasSet := updates[OpSetMetadata]\n\t_, hasUnset := updates[OpUnsetMetadata]\n\tif !hasMerge && !hasSet && !hasUnset {\n\t\treturn nil\n\t}\n\tif _, direct := resolved[\"metadata\"]; direct {\n\t\treturn fmt.Errorf(\"cannot combine a metadata replacement with incremental metadata edits\")\n\t}\n\n\tcurrent := oldIssue.Metadata\n\tif hasMerge {\n\t\tnormalized, err := storage.NormalizeMetadataValue(updates[OpMergeMetadata])\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"invalid %s: %w\", OpMergeMetadata, err)\n\t\t}\n\t\tmerged, err := storage.MergeMetadataJSON(current, json.RawMessage(normalized))\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"metadata merge failed: %w\", err)\n\t\t}\n\t\tcurrent = merged\n\t}\n\tif hasSet || hasUnset {\n\t\tunset, err := mergeOpStrings(OpUnsetMetadata, updates[OpUnsetMetadata], hasUnset)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tvar merged json.RawMessage\n\t\tif set, typed := updates[OpSetMetadata].(map[string]json.RawMessage); typed {\n\t\t\tmerged, err = applyTypedMetadataEdits(current, set, unset)\n\t\t} else {\n\t\t\tvar set []string\n\t\t\tset, err = mergeOpStrings(OpSetMetadata, updates[OpSetMetadata], hasSet)","sourceCodeStart":869,"sourceCodeEnd":905,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/issueops/update.go#L869-L905","documentation":"The metadata value supplied for an incremental merge operation failed normalization (storage.NormalizeMetadataValue). The error wraps the normalization failure under \"invalid merge_metadata\" (or the relevant op name), indicating the payload is not usable as metadata input.","triggerScenarios":"Passing OpMergeMetadata with a value that NormalizeMetadataValue rejects — e.g. non-JSON-serializable input, a non-object JSON document (array/string/number), or invalid types inside the object.","commonSituations":"Passing a JSON array or scalar where an object is expected, feeding user input that was never JSON-validated, or a CLI flag carrying malformed JSON.","solutions":["Validate the merge payload is a JSON object before submitting (json.Valid + unmarshal into map[string]any)","Read the wrapped %w cause for the exact normalization problem","Convert array/scalar payloads into key/value objects"],"exampleFix":"// before\nupdates[issueops.OpMergeMetadata] = []string{\"a\", \"b\"} // not an object\n\n// after\nupdates[issueops.OpMergeMetadata] = map[string]any{\"tags\": []string{\"a\", \"b\"}}","handlingStrategy":"validation","validationCode":"func validMergePayload(v any) error {\n    b, err := json.Marshal(v)\n    if err != nil { return err }\n    var obj map[string]any\n    if err := json.Unmarshal(b, &obj); err != nil {\n        return fmt.Errorf(\"merge payload must be a JSON object: %w\", err)\n    }\n    return nil\n}","typeGuard":"func isJSONObject(raw json.RawMessage) bool {\n    var m map[string]json.RawMessage\n    return json.Unmarshal(raw, &m) == nil\n}","tryCatchPattern":"if err := storage.NormalizeMetadataValue(updates[issueops.OpMergeMetadata]); err != nil {\n    return fmt.Errorf(\"rejecting update: merge payload invalid: %w\", err)\n}","preventionTips":["Always send metadata merge payloads as JSON objects","Marshal Go structs with json.Marshal rather than hand-writing JSON","Validate user-supplied JSON before submitting updates"],"tags":["validation","metadata","json"],"backgroundTag":"invalid-metadata-payload","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}