siyuan-note/siyuan · error

found invalid ID [" + invalidID + "]

Error message

found invalid ID [" + invalidID + "]

What it means

DataBlockDOM converts a Markdown string to block DOM for the block-update API and validates that every node ID found in the produced tree matches SiYuan's block ID pattern (a 14-character timestamp-based string). If any node carries an explicit ID that does not match the expected pattern, conversion is aborted and this error is returned with the offending ID embedded in the message. It prevents malformed custom IDs from entering the document tree via /api/block/updateBlock or insert.

Solutions

  1. Remove or correct the invalid {: id="..."} attribute in the markdown payload so all IDs match SiYuan's 14-character ID pattern
  2. Let SiYuan generate IDs itself: strip explicit id attributes from the markdown before sending
  3. Sanitize the payload client-side with the same pattern regex used by ast.IsNodeIDPattern before calling the API

Example fix

// before
markdown := "## Heading {: id=\"my-slug\"}"
// after
markdown := "## Heading" // or use a valid 14-char ID: {: id="20240101120000-abc1234"}
Defensive patterns

Strategy: validation

Validate before calling

function hasValidBlockIds(markdown) {
  return !Array.from(markdown.matchAll(/\{:\s*id="([^"]+)"\}/g))
    .some(([, id]) => !/^\d{14}-[0-9a-z]{7}$/.test(id));
}
if (!hasValidBlockIds(payload.data)) throw new Error("payload contains invalid block IDs");

Type guard

const isValidSiYuanId = (id) => typeof id === "string" && /^\d{14}-[0-9a-z]{7}$/.test(id);

Prevention

When it happens

Trigger: Calling DataBlockDOM (or the updateBlock API with dataType "markdown") with markdown containing kramdown-style explicit block IDs like {: id="custom-id"} or {: id="123"} that are not valid SiYuan IDs (must match the IsNodeIDPattern 14-digit format). The walk stops at the first offending node and reports its ID.

Common situations: Pasting markdown exported from other tools (Logseq, Notion, Obsidian) that embed their own anchor/ID attributes; hand-writing {: id="..."} attributes with arbitrary slugs; programmatically generated markdown reusing foreign identifiers.

Understand the failure class

Background: "invalid id" errors: invalid identifier format — why libraries reject IDs before lookup, and how to fix them — this error's family across 37 libraries.

Related errors


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

Appendix: source

Thrown at kernel/model/block_update.go:198

		blankParagraph := treenode.NewParagraph("")
		ret = luteEngine.RenderNodeBlockDOM(blankParagraph)
	}

	invalidID := ""
	if nil != tree && nil != tree.Root {
		ast.Walk(tree.Root, func(n *ast.Node, entering bool) ast.WalkStatus {
			if !entering {
				return ast.WalkContinue
			}
			if "" != n.ID && !ast.IsNodeIDPattern(n.ID) {
				invalidID = n.ID
				return ast.WalkStop
			}
			return ast.WalkContinue
		})
	}
	if "" != invalidID {
		return "", errors.New("found invalid ID [" + invalidID + "]")
	}
	return
}

func parseBlockUpdateData(data, dataType string, luteEngine *lute.Lute) (ret string, tree *parse.Tree, err error) {
	ret = data
	switch dataType {
	case "markdown":
		ret, err = DataBlockDOM(data, luteEngine)
		if err != nil {
			err = fmt.Errorf("data block DOM failed: %w", err)
			return
		}
	case "dom":
	default:
		err = fmt.Errorf("unsupported block data type [%s]", dataType)
		return
	}

View on GitHub (pinned to 9f775e8a12)