siyuan-note/siyuan · error
invalid block structure: %s [%s] is not a content block
Error message
invalid block structure: %s [%s] is not a content block
What it means
Returned by invalidBlockNodeError in kernel/treenode/block_structure.go when ValidateBlockSubtree / ValidateBlockPlacement / ValidateBlockReplacement receives a node that is not a content block — i.e. isContentBlock returns false because the node is nil, is not a block (node.IsBlock() false), or is a NodeKramdownBlockIAL node (the IAL marker, not real content).
Source
Thrown at kernel/treenode/block_structure.go:107
return invalidBlockContainmentError(parent, newNode)
}
return ValidateBlockSubtree(newNode)
}
func isContentBlock(node *ast.Node) bool {
return nil != node && node.IsBlock() && ast.NodeKramdownBlockIAL != node.Type
}
func invalidBlockContainmentError(parent, child *ast.Node) error {
return fmt.Errorf("invalid block structure: %s [%s] cannot contain %s [%s]",
parent.Type.String(), parent.ID, child.Type.String(), child.ID)
}
func invalidBlockNodeError(node *ast.Node) error {
if nil == node {
return fmt.Errorf("invalid block structure: block node is nil")
}
return fmt.Errorf("invalid block structure: %s [%s] is not a content block", node.Type.String(), node.ID)
}
View on GitHub (pinned to 251596fc0d)
Solutions
- Guard with isContentBlock (or node != nil && node.IsBlock() && node.Type != ast.NodeKramdownBlockIAL) before calling validate.
- Make sure you are passing a block-level node (paragraph, heading, list, list-item, blockquote, code block, document, etc.), not an inline or IAL node.
- If the node came from JSON, verify the Type field maps to an ast.NodeType that IsBlock() returns true for.
Example fix
// before
err := treenode.ValidateBlockSubtree(ialNode) // ialNode.Type == ast.NodeKramdownBlockIAL
// after
if node != nil && node.IsBlock() && node.Type != ast.NodeKramdownBlockIAL {
err = treenode.ValidateBlockSubtree(node)
} Defensive patterns
Strategy: type-guard
Type guard
func isContentBlock(n *ast.Node) bool {
return n != nil && n.IsBlock() && n.Type != ast.NodeKramdownBlockIAL
}
// usage
if !isContentBlock(node) {
return fmt.Errorf("node is nil, inline, or an IAL marker; cannot validate as a block")
}
err := treenode.ValidateBlockSubtree(node) Prevention
- Never pass inline nodes (text spans, marks) or NodeKramdownBlockIAL nodes to treenode.ValidateBlock*.
- Filter walks with node.IsBlock() before validation.
- When deserialising nodes from JSON, assert the Type resolves to a block-level NodeType.
When it happens
Trigger: Passing a nil node, an inline (non-block) node, or a NodeKramdownBlockIAL node as the root argument to any treenode.ValidateBlock* function. Typically happens in code that walks the raw AST and hands the wrong node type to validation.
Common situations: A plugin/agent picks an inline span or the IAL attribute node out of the tree and tries to validate it as a block; a nil check is missing before calling validate; deserialised JSON node whose Type was not set to a block type.
Related errors
- invalid block structure: %s [%s] cannot contain %s [%s]
- --id is required
- block [%s] is not an instance of attribute view [%s]
- block not found
- parent block not found: %s
AI-assisted analysis of siyuan-note/siyuan@251596fc0d (2026-08-12).
Data as JSON: /api/errors/70f124eff4cafedf.
Report an issue: GitHub.