siyuan-note/siyuan · error
not a code block
Error message
not a code block
What it means
ExportCodeBlock exports a code block's content as a .txt file. Before exporting it checks that the node resolved from the tree is actually of type ast.NodeCodeBlock; if the blockID points to any other node type (paragraph, heading, table, etc.) it returns errors.New("not a code block"). The API contract simply does not apply to non-code blocks.
Solutions
- Check the block type before calling: load the tree node and confirm it is a code block.
- In the UI, only invoke the code-block export action from a code block's menu context.
- For batch exports, filter IDs by type via SQL (type = 'NodeCodeBlock') before calling the API.
Example fix
// before
ExportCodeBlock(anyBlockID)
// after
if node := treenode.GetNodeInTree(tree, blockID); node != nil && node.Type == ast.NodeCodeBlock {
ExportCodeBlock(blockID)
} Defensive patterns
Strategy: validation
Validate before calling
node := treenode.GetNodeInTree(tree, blockID)
if node == nil || node.Type != ast.NodeCodeBlock {
return errors.New("block is not a code block")
}
filePath, err := model.ExportCodeBlock(blockID) Type guard
func isCodeBlock(node *ast.Node) bool { return node != nil && node.Type == ast.NodeCodeBlock } Try / catch
if err != nil {
if err.Error() == "not a code block" {
// fall back to another export path for non-code blocks
}
return err
} Prevention
- Filter candidate block IDs by SQL type 'NodeCodeBlock' before batch export
- Only wire the export action into code-block context menus
- Re-validate block type after any block conversion operation
When it happens
Trigger: Calling ExportCodeBlock(blockID) with a blockID that exists in the tree but whose node.Type != ast.NodeCodeBlock — e.g. passing a document ID, heading ID, or paragraph ID.
Common situations: Frontend/plugin code assumes the focused block is a code block; stale IDs after the block was converted to another type; batch export scripts iterating block IDs without filtering by type.
Understand the failure class
Background: "is not a compatible type" / "cannot merge" errors: when a value's type doesn't match what the library requires — this error's family across 65 libraries.
Related errors
- export artifact [ ] is a directory
- export path is outside export directory
- Field [ ] is required
- Field [ ] must not be empty
- Field [ ] should be of type [ ]
AI-assisted analysis of siyuan-note/siyuan@9f775e8a12 (2026-09-19).
Data as JSON: /api/errors/568320ebd72f8660.
Report an issue: GitHub.
Appendix: source
Thrown at kernel/model/export.go:112
return
}
func ExportCodeBlock(blockID string) (filePath string, err error) {
// Supports exporting a code block as a file https://github.com/siyuan-note/siyuan/pull/16774
err = withExportReadLockByBlockID(blockID, func() error {
tree, _ := LoadTreeByBlockID(blockID)
if nil == tree {
return ErrBlockNotFound
}
node := treenode.GetNodeInTree(tree, blockID)
if nil == node {
return ErrBlockNotFound
}
if ast.NodeCodeBlock != node.Type {
return errors.New("not a code block")
}
code := node.ChildByType(ast.NodeCodeBlockCode)
if nil == code {
return errors.New("code block has no code node")
}
name := tree.Root.IALAttr("title") + "-" + util.CurrentTimeSecondsStr() + ".txt"
name = util.FilterFileName(name)
exportFolder := filepath.Join(util.TempDir, "export")
// 加密笔记本的导出归入 boxID 子目录,确保 LockBox 清理和服务端校验锁定状态
if IsEncryptedBox(tree.Box) {
exportFolder = filepath.Join(exportFolder, tree.Box)
}
exportFolder = filepath.Join(exportFolder, "code")
if mkdirErr := os.MkdirAll(exportFolder, 0755); mkdirErr != nil {
return mkdirErr
}View on GitHub (pinned to 9f775e8a12)