{"record":{"id":"a9ca26414f1a9982","repo":"siyuan-note/siyuan","slug":"empty-json-value","errorCode":null,"errorMessage":"empty JSON value","messagePattern":"empty JSON value","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/apicontract/transaction_decode.go","lineNumber":58,"sourceCode":"\t}\n\tif request.App, err = legacyField[string](fields, \"app\", \"String\", false); err != nil {\n\t\treturn request, err\n\t}\n\tif request.Session, err = legacyField[string](fields, \"session\", \"String\", false); err != nil {\n\t\treturn request, err\n\t}\n\trequest.TransactionJSON, request.DecodeError = normalizeTransactionJSON(fields[\"transactions\"])\n\tif request.DecodeError == nil {\n\t\trequest.DecodeError = json.Unmarshal(request.TransactionJSON, &request.Transactions)\n\t}\n\treturn request, nil\n}\n\n// normalizeTransactionJSON 保留入口先按 JSON 数字读取、再绑定事务结构的数值语义。\nfunc normalizeTransactionJSON(data []byte) ([]byte, error) {\n\tdata = bytes.TrimSpace(data)\n\tif len(data) == 0 {\n\t\treturn nil, fmt.Errorf(\"empty JSON value\")\n\t}\n\tswitch data[0] {\n\tcase '{':\n\t\tvar fields map[string]json.RawMessage\n\t\tif err := json.Unmarshal(data, &fields); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tfor key, value := range fields {\n\t\t\tnormalized, err := normalizeTransactionJSON(value)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\tfields[key] = normalized\n\t\t}\n\t\treturn json.Marshal(fields)\n\tcase '[':\n\t\tvar items []json.RawMessage\n\t\tif err := json.Unmarshal(data, &items); err != nil {","sourceCodeStart":40,"sourceCodeEnd":76,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/9f775e8a12daef8255556097396f9b2739078892/kernel/apicontract/transaction_decode.go#L40-L76","documentation":"normalizeTransactionJSON validates each entry of the transactions array before binding it to the transaction struct. An entry that is empty (zero-length or whitespace-only after trimming) cannot be interpreted as a JSON value and is rejected with this error, preserving the entry-first numeric-read semantics.","triggerScenarios":"A transactions array containing \"\" or \"   \" string entries that get passed through as raw bytes; slicing/serialization bugs producing empty RawMessage elements; corrupted request bodies from proxies.","commonSituations":"Custom clients constructing transactions arrays from joined/serialized fragments where an empty fragment slips in; template-generated payloads with unfilled placeholders; JSON round-trip bugs dropping element content.","solutions":["Remove empty/whitespace entries from the transactions array before sending","Make each array element a complete JSON object such as {\"doOperations\":[...],\"undoOperations\":[...]}","Audit the code that builds the array so it never appends undefined/empty items","Validate the payload client-side with a check that every element parses as JSON"],"exampleFix":"// before\ntransactions: [\"\", JSON.stringify(tx)]\n// after\ntransactions: [JSON.stringify(tx)]","handlingStrategy":"validation","validationCode":"transactions = transactions.filter(t => t != null && JSON.stringify(t).trim() !== \"\");","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Filter out null/undefined/empty entries before building the transactions array","Each array element must be a complete JSON object with doOperations/undoOperations","Audit serialization code that joins fragments, which can emit empty strings"],"tags":["json","validation","api"],"backgroundTag":"json-parse-error","analyzedSha":"9f775e8a12daef8255556097396f9b2739078892","analyzedAt":"2026-09-19T03:17:15.984Z","contentChangedAt":"2026-09-19T03:17:15.984Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}