siyuan-note/siyuan · error

not a code block

Error message

not a code block

What it means

Returned by ExportCodeBlock when the block referenced by blockID loads successfully but its AST node type is not ast.NodeCodeBlock. The function specifically exports code-block content as a .txt file, so any other block type (paragraph, heading, list, etc.) is rejected. This is a type-mismatch guard, not a corruption indicator.

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 251596fc0d)

Solutions

  1. Verify the blockID resolves to a NodeCodeBlock before calling ExportCodeBlock — check the block type via the block-tree or SQL.
  2. Ensure the UI context menu for 'export code block' only appears when the selected block is a code block.
  3. If exporting arbitrary block content is needed, use the general export functions (Export2HTML/Markdown) instead of ExportCodeBlock.

Example fix

// before
path, err := model.ExportCodeBlock(blockID)
// after — verify type first
bt := treenode.GetBlockTree(blockID)
if bt == nil || bt.Type != "NodeCodeBlock" {
    return errors.New("selected block is not a code block")
}
path, err := model.ExportCodeBlock(blockID)
Defensive patterns

Strategy: type-guard

Validate before calling

// Verify block is a code block before exporting
bt := treenode.GetBlockTree(blockID)
if bt == nil || bt.Type != "NodeCodeBlock" {
    return errors.New("block is not a code block")
}

Type guard

func isCodeBlock(blockID string) bool {
    bt := treenode.GetBlockTree(blockID)
    return bt != nil && bt.Type == "NodeCodeBlock"
}

Prevention

When it happens

Trigger: ExportCodeBlock is called via the API/UI with a blockID that points to a non-code-block node. The frontend 'export code block' menu item was somehow triggered on a non-code block, or an API caller passed the wrong block ID.

Common situations: API caller passed a document-level block ID instead of the inner code-block ID. Plugin or automation invoked the export endpoint with a paragraph or heading ID. Frontend bug sent the wrong block reference.

Related errors


AI-assisted analysis of siyuan-note/siyuan@251596fc0d (2026-08-12). Data as JSON: /api/errors/568320ebd72f8660. Report an issue: GitHub.