{"record":{"id":"9a10191e28687eb3","repo":"siyuan-note/siyuan","slug":"field-ids-should-contain-strings","errorCode":null,"errorMessage":"Field [ids] should contain strings","messagePattern":"Field \\[ids\\] should contain strings","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/apicontract/block_remaining.go","lineNumber":168,"sourceCode":"\treturn\n}\n\nfunc decodeHeadingLevel(reader io.Reader) (request HeadingLevelRequest, err error) {\n\tfields, err := blockRequestFields(reader, \"/api/block/getHeadingLevelTransaction\")\n\tif err != nil {\n\t\treturn request, err\n\t}\n\tif len(fields[\"level\"]) == 0 || bytes.Equal(fields[\"level\"], []byte(\"null\")) || json.Unmarshal(fields[\"level\"], &request.Level) != nil {\n\t\treturn request, errors.New(\"Field [level] should be of type [Number]\")\n\t}\n\tvar entries []json.RawMessage\n\tif raw := fields[\"ids\"]; len(raw) > 0 && !bytes.Equal(raw, []byte(\"null\")) && json.Unmarshal(raw, &entries) == nil {\n\t\trequest.IDs = []string{}\n\t\tseen := map[string]bool{}\n\t\tfor _, entry := range entries {\n\t\t\tvar id string\n\t\t\tif bytes.Equal(entry, []byte(\"null\")) || json.Unmarshal(entry, &id) != nil {\n\t\t\t\treturn request, errors.New(\"Field [ids] should contain strings\")\n\t\t\t}\n\t\t\tif !seen[id] {\n\t\t\t\trequest.IDs = append(request.IDs, id)\n\t\t\t\tseen[id] = true\n\t\t\t}\n\t\t}\n\t} else if len(fields[\"id\"]) == 0 || bytes.Equal(fields[\"id\"], []byte(\"null\")) || json.Unmarshal(fields[\"id\"], &request.ID) != nil {\n\t\treturn request, errors.New(\"Field [id] should be of type [String]\")\n\t}\n\treturn\n}\n","sourceCodeStart":150,"sourceCodeEnd":180,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/9f775e8a12daef8255556097396f9b2739078892/kernel/apicontract/block_remaining.go#L150-L180","documentation":"In /api/block/getHeadingLevelTransaction, the optional ids array is validated element-by-element: every element must be a JSON string (null elements are also rejected, unlike some other endpoints). The kernel raises this error when any element fails to unmarshal into a string. This branch only runs when ids is present and is a valid JSON array.","triggerScenarios":"POST /api/block/getHeadingLevelTransaction with \"ids\": [\"id1\", 42], \"ids\": [null], or \"ids\": [{\"id\":\"...\"}].","commonSituations":"Mixing numeric row IDs from a database view with block ID strings; null elements appearing after JSON round-trips; passing objects copied from another API's response shape.","solutions":["Ensure every element is a plain JSON string block ID","Filter or map non-strings: ids.filter(id => typeof id === 'string' && id)","If a single block is targeted, use the scalar \"id\" field instead of the ids array"],"exampleFix":"// before\nawait fetchPost(\"/api/block/getHeadingLevelTransaction\", { ids: [id1, id2], level: 2 }); // id2 is a number\n// after\nawait fetchPost(\"/api/block/getHeadingLevelTransaction\", { ids: [id1, String(id2)], level: 2 });","handlingStrategy":"validation","validationCode":"if (payload.ids && !payload.ids.every(id => typeof id === 'string' && id)) throw new TypeError('ids must all be non-empty strings');","typeGuard":"const isBlockIdArray = (v) => Array.isArray(v) && v.every((x) => typeof x === 'string' && /^\\d{14}-[0-9a-z]{7}$/.test(x));","tryCatchPattern":"try { await fetchPost('/api/block/getHeadingLevelTransaction', payload); } catch (e) { if (/Field \\[ids\\] should contain strings/.test(e?.message)) { payload.ids = payload.ids.filter(id => typeof id === 'string' && id); /* retry */ } }","preventionTips":["Map all ID sources through String() before pushing into the ids array","Filter out null/undefined before serializing sparse arrays","Validate the block-ID format (\\d{14}-[0-9a-z]{7}) at collection time"],"tags":["api","validation","array-elements"],"backgroundTag":"invalid-argument-value","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"}