siyuan-note/siyuan · error

createEmptyParagraph must be a boolean

Error message

createEmptyParagraph must be a boolean

What it means

UnmarshalJSON for the block delete/transaction options rejects a decoded payload whose createEmptyParagraph field is absent or not a boolean. The decoder runs with DisallowUnknownFields and then explicitly requires the pointer field to be non-nil, so a missing key or a non-boolean value (e.g. a string "true") fails. This enforces an explicit API contract for the transaction options shape.

Solutions

  1. Add createEmptyParagraph as a real JSON boolean (true or false) to the options object
  2. Remove any JSON null value for the field and replace it with an explicit boolean
  3. Check for typos or case mismatches in the key name (the decoder also rejects unknown fields)
  4. Update older client code or generated schemas that predate the mandatory field

Example fix

// before
{"actions":[{"action":"delete","srcIDs":["20240101120000-abc"],"createEmptyParagraph":"true"}]}
// after
{"actions":[{"action":"delete","srcIDs":["20240101120000-abc"],"createEmptyParagraph":true}]}
Defensive patterns

Strategy: validation

Validate before calling

function isBool(v) { return typeof v === "boolean"; }
if (!payload.actions.every(a => a.createEmptyParagraph === undefined || isBool(a.createEmptyParagraph))) throw new Error("createEmptyParagraph must be boolean");

Type guard

function hasBooleanFlag(o) { return typeof o?.createEmptyParagraph === "boolean"; }

Try / catch

try { await postTransaction(payload); } catch (e) { if (String(e).includes("createEmptyParagraph must be a boolean")) { payload.actions.forEach(a => { if (typeof a.createEmptyParagraph !== "boolean") a.createEmptyParagraph = false; }); await postTransaction(payload); } else throw e; }

Prevention

When it happens

Trigger: Calling the block transaction API with a JSON payload that omits createEmptyParagraph, or sends it as a non-boolean (string, number, object, or JSON null). JSON null is notable: Go decodes null into a *bool as nil, so {"createEmptyParagraph": null} also triggers this error.

Common situations: Hand-written client payloads where the flag was considered optional; older clients built before the field became mandatory; templated request bodies where the key was accidentally dropped; sending quoted booleans ("true") from loosely typed languages.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


AI-assisted analysis of siyuan-note/siyuan@9f775e8a12 (2026-09-19). Data as JSON: /api/errors/8b0186fa01b0a639. Report an issue: GitHub.

Appendix: source

Thrown at kernel/apicontract/block_transaction.go:56

func (d *BlockOperationData) UnmarshalJSON(data []byte) error {
	data = bytes.TrimSpace(data)
	*d = BlockOperationData{}
	if bytes.Equal(data, []byte("null")) {
		return nil
	}
	if len(data) > 0 && data[0] == '"' {
		return json.Unmarshal(data, &d.text)
	}
	var options struct {
		CreateEmptyParagraph *bool `json:"createEmptyParagraph"`
	}
	decoder := json.NewDecoder(bytes.NewReader(data))
	decoder.DisallowUnknownFields()
	if err := decoder.Decode(&options); err != nil {
		return err
	}
	if options.CreateEmptyParagraph == nil {
		return fmt.Errorf("createEmptyParagraph must be a boolean")
	}
	d.deleteOptions = &BlockDeleteData{CreateEmptyParagraph: *options.CreateEmptyParagraph}
	return nil
}

// BlockOperationResult 保留块操作返回的文本、块 ID 数组和空值三种载荷。
type BlockOperationResult struct {
	text *string
	ids  []string
}

func (r BlockOperationResult) MarshalJSON() ([]byte, error) {
	if r.text != nil {
		return json.Marshal(*r.text)
	}
	return json.Marshal(r.ids)
}

View on GitHub (pinned to 9f775e8a12)