{"record":{"id":"568320ebd72f8660","repo":"siyuan-note/siyuan","slug":"not-a-code-block","errorCode":null,"errorMessage":"not a code block","messagePattern":"not a code block","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/model/export.go","lineNumber":112,"sourceCode":"\treturn\n}\n\nfunc ExportCodeBlock(blockID string) (filePath string, err error) {\n\t// Supports exporting a code block as a file https://github.com/siyuan-note/siyuan/pull/16774\n\n\terr = withExportReadLockByBlockID(blockID, func() error {\n\t\ttree, _ := LoadTreeByBlockID(blockID)\n\t\tif nil == tree {\n\t\t\treturn ErrBlockNotFound\n\t\t}\n\n\t\tnode := treenode.GetNodeInTree(tree, blockID)\n\t\tif nil == node {\n\t\t\treturn ErrBlockNotFound\n\t\t}\n\n\t\tif ast.NodeCodeBlock != node.Type {\n\t\t\treturn errors.New(\"not a code block\")\n\t\t}\n\n\t\tcode := node.ChildByType(ast.NodeCodeBlockCode)\n\t\tif nil == code {\n\t\t\treturn errors.New(\"code block has no code node\")\n\t\t}\n\n\t\tname := tree.Root.IALAttr(\"title\") + \"-\" + util.CurrentTimeSecondsStr() + \".txt\"\n\t\tname = util.FilterFileName(name)\n\t\texportFolder := filepath.Join(util.TempDir, \"export\")\n\t\t// 加密笔记本的导出归入 boxID 子目录，确保 LockBox 清理和服务端校验锁定状态\n\t\tif IsEncryptedBox(tree.Box) {\n\t\t\texportFolder = filepath.Join(exportFolder, tree.Box)\n\t\t}\n\t\texportFolder = filepath.Join(exportFolder, \"code\")\n\t\tif mkdirErr := os.MkdirAll(exportFolder, 0755); mkdirErr != nil {\n\t\t\treturn mkdirErr\n\t\t}","sourceCodeStart":94,"sourceCodeEnd":130,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/251596fc0de2f9528c00c224252fd073a99973f4/kernel/model/export.go#L94-L130","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Verify the blockID resolves to a NodeCodeBlock before calling ExportCodeBlock — check the block type via the block-tree or SQL.","Ensure the UI context menu for 'export code block' only appears when the selected block is a code block.","If exporting arbitrary block content is needed, use the general export functions (Export2HTML/Markdown) instead of ExportCodeBlock."],"exampleFix":"// before\npath, err := model.ExportCodeBlock(blockID)\n// after — verify type first\nbt := treenode.GetBlockTree(blockID)\nif bt == nil || bt.Type != \"NodeCodeBlock\" {\n    return errors.New(\"selected block is not a code block\")\n}\npath, err := model.ExportCodeBlock(blockID)","handlingStrategy":"type-guard","validationCode":"// Verify block is a code block before exporting\nbt := treenode.GetBlockTree(blockID)\nif bt == nil || bt.Type != \"NodeCodeBlock\" {\n    return errors.New(\"block is not a code block\")\n}","typeGuard":"func isCodeBlock(blockID string) bool {\n    bt := treenode.GetBlockTree(blockID)\n    return bt != nil && bt.Type == \"NodeCodeBlock\"\n}","tryCatchPattern":null,"preventionTips":["Only show the 'export code block' menu item when the context block is a code block.","API callers should validate the block type before calling ExportCodeBlock.","Use general export functions for non-code content."],"tags":["export","code-block","ast","type-validation"],"backgroundTag":null,"analyzedSha":"251596fc0de2f9528c00c224252fd073a99973f4","analyzedAt":"2026-08-12T21:18:37.123Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}