{"record":{"id":"fae5cb5b1e9f86d1","repo":"siyuan-note/siyuan","slug":"invalid-heading-conversion-parameters","errorCode":null,"errorMessage":"invalid heading conversion parameters","messagePattern":"invalid heading conversion parameters","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/apicontract/block_remaining.go","lineNumber":49,"sourceCode":"\tSource          int    `json:\"source\" api:\"optional,nullable\"`\n\tTarget          int    `json:\"target\" api:\"optional,nullable\"`\n\tWithSubheadings bool   `json:\"withSubheadings\" api:\"optional,nullable\"`\n}\n\ntype DocHeadingLevelData struct {\n\tCounts               []int             `json:\"counts\" api:\"nonnullable\"`\n\tWithSubheadingCounts []int             `json:\"withSubheadingCounts\" api:\"nonnullable\"`\n\tTitle                string            `json:\"title\"`\n\tTransaction          *BlockTransaction `json:\"transaction\"`\n}\n\n// 特殊解码仅保留这些入口的条件参数和结构体绑定语义，响应仍受各自的契约约束。\nfunc init() {\n\tCheckBlockRef.decodeRequest = decodeCheckBlockRef\n\tGetHeadingLevelTransaction.decodeRequest = decodeHeadingLevel\n\tGetDocHeadingLevelTransaction.decodeRequest = func(reader io.Reader) (request DocHeadingLevelRequest, err error) {\n\t\tif json.NewDecoder(reader).Decode(&request) != nil {\n\t\t\terr = errors.New(\"invalid heading conversion parameters\")\n\t\t}\n\t\treturn\n\t}\n}\n\nfunc blockRequestFields(reader io.Reader, path string) (fields map[string]json.RawMessage, err error) {\n\terr = json.NewDecoder(reader).Decode(&fields)\n\tif err != nil {\n\t\tif errors.Is(err, io.EOF) {\n\t\t\terr = errors.New(\"the request body is empty or truncated (EOF)\")\n\t\t}\n\t\terr = fmt.Errorf(\"Parses request [%s] failed: %s\", path, err)\n\t}\n\treturn\n}\n\nfunc decodeCheckBlockRef(reader io.Reader) (request CheckBlockRefRequest, err error) {\n\tfields, err := blockRequestFields(reader, \"/api/block/checkBlockRef\")","sourceCodeStart":31,"sourceCodeEnd":67,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/9f775e8a12daef8255556097396f9b2739078892/kernel/apicontract/block_remaining.go#L31-L67","documentation":"The doc heading-level transaction endpoint installs a custom decoder that binds the request directly to the DocHeadingLevelRequest struct. If the body cannot be decoded into that struct, it collapses every JSON error into the single message 'invalid heading conversion parameters', intentionally hiding internal parse details.","triggerScenarios":"POSTing to the doc heading-level transaction endpoint with malformed JSON, wrong field types (e.g. a string where the struct expects a number/array), missing required struct fields the decoder cannot satisfy, or an empty body.","commonSituations":"Clients changing heading levels whose payload drifted from the current struct shape after an API update, sending nested objects where an array is expected, or building the payload by string concatenation instead of a JSON serializer.","solutions":["Serialize the payload with a JSON library instead of manual string building and compare against the expected request shape.","Verify each field's type matches DocHeadingLevelRequest (IDs as strings, levels/positions as numbers).","Send a minimal valid request first, then add fields incrementally to find the offender.","Check the API contract/docs for the current field names — they may have changed between versions."],"exampleFix":"// before\n{\"id\": 123, \"level\": \"2\"}\n// after\n{\"id\": \"20240101120000-abcdefg\", \"level\": 2}","handlingStrategy":"validation","validationCode":"const requiredShape = { id: \"string\", level: \"number\" };\nfor (const [k, t] of Object.entries(requiredShape)) {\n  if (typeof payload[k] !== t) { throw new Error(`heading payload field ${k} must be ${t}`); }\n}","typeGuard":"const isHeadingPayload = (p) => typeof p?.id === \"string\" && Number.isFinite(p?.level);","tryCatchPattern":"try {\n  await post(headingTxEndpoint, payload);\n} catch (e) {\n  if (String(e.message).includes(\"invalid heading conversion parameters\")) {\n    console.error(\"Heading payload rejected; expected DocHeadingLevelRequest shape:\", payload);\n  } else { throw e; }\n}","preventionTips":["Build heading payloads with a typed serializer, not string templates","Keep the client request type in sync with the API contract","Send a minimal valid request first when debugging","Log the full payload on rejection since the server hides parse details"],"tags":["json","validation","heading","api"],"backgroundTag":"json-decode-failed","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"}