{"record":{"id":"70f124eff4cafedf","repo":"siyuan-note/siyuan","slug":"invalid-block-structure-s-s-is-not-a-content","errorCode":null,"errorMessage":"invalid block structure: %s [%s] is not a content block","messagePattern":"invalid block structure: (.+?) \\[(.+?)\\] is not a content block","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/treenode/block_structure.go","lineNumber":244,"sourceCode":"\t\titem.ListData.Marker = []byte(strconv.Itoa(num) + string(delimiter))\n\t\tnum++\n\t}\n}\n\nfunc isContentBlock(node *ast.Node) bool {\n\treturn nil != node && node.IsBlock() && ast.NodeKramdownBlockIAL != node.Type\n}\n\nfunc invalidBlockContainmentError(parent, child *ast.Node) error {\n\treturn fmt.Errorf(\"invalid block structure: %s [%s] cannot contain %s [%s]\",\n\t\tparent.Type.String(), parent.ID, child.Type.String(), child.ID)\n}\n\nfunc invalidBlockNodeError(node *ast.Node) error {\n\tif nil == node {\n\t\treturn fmt.Errorf(\"invalid block structure: block node is nil\")\n\t}\n\treturn fmt.Errorf(\"invalid block structure: %s [%s] is not a content block\", node.Type.String(), node.ID)\n}\n","sourceCodeStart":226,"sourceCodeEnd":246,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/9f775e8a12daef8255556097396f9b2739078892/kernel/treenode/block_structure.go#L226-L246","documentation":"invalidBlockNodeError returns this message when the node exists but is not a content block (isContentBlock requires node.IsBlock() and excludes Kramdown IAL nodes). Placement/replacement validation only accepts actual content blocks as operands; markers such as IAL attribute nodes or non-block nodes are rejected. The node type and ID are included for diagnosis.","triggerScenarios":"Passing an IAL node, marker, or other non-block AST node as the parent/child to ValidateBlockPlacement/ValidateBlockReplacement; locating a node by position that lands on an IAL rather than the block itself.","commonSituations":"Plugin code traversing the AST by child index and hitting IAL nodes that follow block nodes; ID lookups resolving to structural markers after document edits.","solutions":["Skip Kramdown IAL / non-block nodes when resolving the target node (advance to the next node where node.IsBlock() is true)","Use ID-based lookup (FindByID) rather than positional traversal to obtain the block node","Check isContentBlock-equivalent conditions before calling the validator"],"exampleFix":"// before\nnode := tree.Root.Children[idx] // may be an IAL node\n// after\nnode := tree.Root.Children[idx]\nfor nil != node && (!node.IsBlock() || ast.NodeKramdownBlockIAL == node.Type) {\n    node = node.Next\n}","handlingStrategy":"type-guard","validationCode":"function isContentBlock(n) { return n != null && n.IsBlock === true && n.Type !== \"NodeKramdownBlockIAL\"; }\nif (!isContentBlock(node)) throw new Error(\"target is not a content block\");","typeGuard":"function isContentBlock(n) { return n != null && n.IsBlock === true && n.Type !== \"NodeKramdownBlockIAL\"; }","tryCatchPattern":null,"preventionTips":["When traversing AST children, skip IAL/marker nodes to land on real blocks","Prefer ID-based lookups over positional indices","Assert node type before passing it to placement APIs"],"tags":["document","structure","validation"],"backgroundTag":"invalid-argument-value","analyzedSha":"9f775e8a12daef8255556097396f9b2739078892","analyzedAt":"2026-09-19T03:17:15.984Z","contentChangedAt":"2026-09-19T03:17:15.984Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}