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

  1. Serialize the payload with a JSON library instead of manual string building and compare against the expected request shape.
  2. Verify each field's type matches DocHeadingLevelRequest (IDs as strings, levels/positions as numbers).
  3. Send a minimal valid request first, then add fields incrementally to find the offender.
  4. 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

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


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)