{"record":{"id":"8a56a2820f6e909e","repo":"gastownhall/beads","slug":"w-metadata-value-for-key-q-is-not-valid-json","errorCode":null,"errorMessage":"%w: metadata value for key %q is not valid JSON","messagePattern":"%w: metadata value for key %q is not valid JSON","errorType":"validation","errorClass":"storage.ErrValidation","httpStatus":null,"severity":"error","filePath":"internal/storage/issueops/aggregate.go","lineNumber":241,"sourceCode":"\t\t\t\treturn nil, false, fmt.Errorf(\"%w: metadata merge must be a JSON object\", storage.ErrValidation)\n\t\t\t}\n\t\t\tmerged, err := storage.MergeMetadataJSON(next, patch.Merge.Value)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, false, fmt.Errorf(\"%w: metadata merge: %v\", storage.ErrValidation, err)\n\t\t\t}\n\t\t\tnext = merged\n\t\t}\n\t\tif len(patch.Set) > 0 || len(patch.Unset) > 0 {\n\t\t\tvalues := make(map[string]json.RawMessage)\n\t\t\tif len(next) > 0 && string(next) != \"null\" {\n\t\t\t\tif err := json.Unmarshal(next, &values); err != nil {\n\t\t\t\t\treturn nil, false, fmt.Errorf(\"%w: metadata edits require an object: %v\", storage.ErrValidation, err)\n\t\t\t\t}\n\t\t\t}\n\t\t\tfor _, key := range setKeys {\n\t\t\t\tvalue := patch.Set[key]\n\t\t\t\tif !json.Valid(value) {\n\t\t\t\t\treturn nil, false, fmt.Errorf(\"%w: metadata value for key %q is not valid JSON\", storage.ErrValidation, key)\n\t\t\t\t}\n\t\t\t\tvalues[key] = append(json.RawMessage(nil), value...)\n\t\t\t}\n\t\t\tfor _, key := range patch.Unset {\n\t\t\t\tdelete(values, key)\n\t\t\t}\n\t\t\tencoded, err := json.Marshal(values)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, false, fmt.Errorf(\"%w: encode metadata edits: %v\", storage.ErrValidation, err)\n\t\t\t}\n\t\t\tnext = encoded\n\t\t}\n\t}\n\tif err := ValidateMetadataIfConfigured(next); err != nil {\n\t\treturn nil, false, err\n\t}\n\tchanged, err := metadataChanged(current, next)\n\tif err != nil {","sourceCodeStart":223,"sourceCodeEnd":259,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/issueops/aggregate.go#L223-L259","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\npatch.Set[\"name\"] = json.RawMessage(`Alice`) // invalid JSON\n// after\nb, _ := json.Marshal(\"Alice\")\npatch.Set[\"name\"] = b // \"Alice\" (quoted, valid JSON)","handlingStrategy":"validation","validationCode":"func validSetValues(set map[string]json.RawMessage) error {\n    for k, v := range set {\n        if !json.Valid(v) {\n            return fmt.Errorf(\"key %q: invalid JSON value\", k)\n        }\n    }\n    return nil\n}","typeGuard":null,"tryCatchPattern":"if errors.Is(err, storage.ErrValidation) && strings.Contains(err.Error(), \"not valid JSON\") { /* fix the named key's value, no retry */ }","preventionTips":["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."],"tags":["go","validation","metadata","json"],"backgroundTag":"invalid-json-metadata-value","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}