siyuan-note/siyuan · error
invalid heading conversion parameters
Error message
invalid heading conversion parameters
What it means
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.
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.
Example fix
// before
{"id": 123, "level": "2"}
// after
{"id": "20240101120000-abcdefg", "level": 2} Defensive patterns
Strategy: validation
Validate before calling
const requiredShape = { id: "string", level: "number" };
for (const [k, t] of Object.entries(requiredShape)) {
if (typeof payload[k] !== t) { throw new Error(`heading payload field ${k} must be ${t}`); }
} Type guard
const isHeadingPayload = (p) => typeof p?.id === "string" && Number.isFinite(p?.level);
Try / catch
try {
await post(headingTxEndpoint, payload);
} catch (e) {
if (String(e.message).includes("invalid heading conversion parameters")) {
console.error("Heading payload rejected; expected DocHeadingLevelRequest shape:", payload);
} else { throw e; }
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- element [ ]
- empty JSON value
- entry [ ]
- Field [conf] should be of type [Object]
- Field [ ] has an invalid constant
AI-assisted analysis of siyuan-note/siyuan@9f775e8a12 (2026-09-19).
Data as JSON: /api/errors/fae5cb5b1e9f86d1.
Report an issue: GitHub.
Appendix: source
Thrown at kernel/apicontract/block_remaining.go:49
Source int `json:"source" api:"optional,nullable"`
Target int `json:"target" api:"optional,nullable"`
WithSubheadings bool `json:"withSubheadings" api:"optional,nullable"`
}
type DocHeadingLevelData struct {
Counts []int `json:"counts" api:"nonnullable"`
WithSubheadingCounts []int `json:"withSubheadingCounts" api:"nonnullable"`
Title string `json:"title"`
Transaction *BlockTransaction `json:"transaction"`
}
// 特殊解码仅保留这些入口的条件参数和结构体绑定语义,响应仍受各自的契约约束。
func init() {
CheckBlockRef.decodeRequest = decodeCheckBlockRef
GetHeadingLevelTransaction.decodeRequest = decodeHeadingLevel
GetDocHeadingLevelTransaction.decodeRequest = func(reader io.Reader) (request DocHeadingLevelRequest, err error) {
if json.NewDecoder(reader).Decode(&request) != nil {
err = errors.New("invalid heading conversion parameters")
}
return
}
}
func blockRequestFields(reader io.Reader, path string) (fields map[string]json.RawMessage, err error) {
err = json.NewDecoder(reader).Decode(&fields)
if err != nil {
if errors.Is(err, io.EOF) {
err = errors.New("the request body is empty or truncated (EOF)")
}
err = fmt.Errorf("Parses request [%s] failed: %s", path, err)
}
return
}
func decodeCheckBlockRef(reader io.Reader) (request CheckBlockRefRequest, err error) {
fields, err := blockRequestFields(reader, "/api/block/checkBlockRef")View on GitHub (pinned to 9f775e8a12)