{"record":{"id":"ca3f22db984ea2fd","repo":"gastownhall/beads","slug":"metadata-value-for-key-q-is-not-valid-json","errorCode":null,"errorMessage":"metadata value for key %q is not valid JSON","messagePattern":"metadata value for key %q is not valid JSON","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/issueops/update.go","lineNumber":945,"sourceCode":"\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\tkeys := make([]string, 0, len(set))\n\tfor key := range set {\n\t\tkeys = append(keys, key)\n\t}\n\tsort.Strings(keys)\n\tfor _, key := range keys {\n\t\tif err := storage.ValidateMetadataKey(key); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tif !json.Valid(set[key]) {\n\t\t\treturn nil, fmt.Errorf(\"metadata value for key %q is not valid JSON\", key)\n\t\t}\n\t\tdata[key] = set[key]\n\t}\n\tfor _, key := range unset {\n\t\tif err := storage.ValidateMetadataKey(key); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tdelete(data, key)\n\t}\n\tresult, err := json.Marshal(data)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to marshal metadata: %w\", err)\n\t}\n\treturn json.RawMessage(result), nil\n}\n\n// resolveNotesAppendOp folds OpAppendNotes into a concrete \"notes\" value on\n// resolved, appending to oldIssue.Notes (read in the same mutation transaction).","sourceCodeStart":927,"sourceCodeEnd":963,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/issueops/update.go#L927-L963","documentation":"A value supplied for a metadata set operation is not valid JSON. Each value in a typed set edit must be a valid JSON document (json.Valid check) before it is written into the metadata object.","triggerScenarios":"Calling an update with OpSetMetadata whose map contains a json.RawMessage (or equivalent) that is malformed JSON, e.g. RawMessage built from a non-serialized Go string.","commonSituations":"Constructing json.RawMessage from raw user text without json.Marshal, hand-writing JSON in CLI args with quoting errors, or templating that interpolates Go values as strings instead of JSON.","solutions":["Run json.Valid (or json.Marshal the value) before placing it in the set map","Build values with json.Marshal(value) instead of hand-crafted strings","For string values, ensure they are quoted JSON strings: json.RawMessage(`\"text\"`), not json.RawMessage(`text`)"],"exampleFix":"// before\nset[\"note\"] = json.RawMessage(`hello world`) // invalid JSON\n\n// after\nb, _ := json.Marshal(\"hello world\")\nset[\"note\"] = b // \"hello world\"","handlingStrategy":"validation","validationCode":"for key, val := range set {\n    if !json.Valid(val) {\n        return fmt.Errorf(\"key %q: value is not valid JSON\", key)\n    }\n}","typeGuard":"func validJSONValue(raw json.RawMessage) bool { return json.Valid(raw) }","tryCatchPattern":"if err := issueops.ResolveMergeOps(issue, updates, resolved); err != nil {\n    if strings.Contains(err.Error(), \"is not valid JSON\") {\n        return fmt.Errorf(\"fix the metadata value before retrying: %w\", err)\n    }\n}","preventionTips":["Build values with json.Marshal instead of string literals","Remember strings need quotes in JSON: \"\\\"text\\\"\" not \"text\"","Run json.Valid on all externally supplied values before submitting"],"tags":["metadata","json","validation"],"backgroundTag":"invalid-json-value","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}