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
- Remove or correct the invalid {: id="..."} attribute in the markdown payload so all IDs match SiYuan's 14-character ID pattern
- Let SiYuan generate IDs itself: strip explicit id attributes from the markdown before sending
- 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
- Strip explicit {: id="..."} attributes from external markdown before API calls
- Regenerate IDs with SiYuan instead of importing foreign identifiers
- Add a pre-send regex validation of any hand-crafted block IDs
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
- Do not include Markdown syntax marker
- AI editor action must not be empty
- block [ ] type is locked: expected , got
- block swap requires two non-document blocks
- Bookmark cannot be empty
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)