{"record":{"id":"55e7aa243f680684","repo":"siyuan-note/siyuan","slug":"the-top-level-notebook-document-cannot-be-removed-55e7aa","errorCode":null,"errorMessage":"The top-level notebook document cannot be removed or moved","messagePattern":"The top-level notebook document cannot be removed or moved","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/model/heading.go","lineNumber":188,"sourceCode":"\t\t}()\n\t}\n\treturn\n}\n\nfunc Doc2Heading(srcID, targetID string, after bool) (srcTreeBox, srcTreePath string, err error) {\n\tif !ast.IsNodeIDPattern(srcID) || !ast.IsNodeIDPattern(targetID) {\n\t\treturn\n\t}\n\n\tFlushTxQueue()\n\n\tsrcTree, _ := LoadTreeByBlockID(srcID)\n\tif nil == srcTree {\n\t\terr = ErrBlockNotFound\n\t\treturn\n\t}\n\tif IsBoxDoc(srcTree.Box, srcTree.ID) {\n\t\terr = errors.New(Conf.Language(341))\n\t\treturn\n\t}\n\n\tsubDir := filepath.Join(util.DataDir, srcTree.Box, strings.TrimSuffix(srcTree.Path, \".sy\"))\n\tif gulu.File.IsDir(subDir) {\n\t\tif !util.IsEmptyDir(subDir) {\n\t\t\terr = errors.New(Conf.Language(20))\n\t\t\treturn\n\t\t}\n\n\t\tif removeErr := os.Remove(subDir); nil != removeErr { // 移除空文件夹不会有副作用\n\t\t\tlogging.LogWarnf(\"remove empty dir [%s] failed: %s\", subDir, removeErr)\n\t\t}\n\t}\n\n\tif nil == treenode.GetBlockTree(targetID) {\n\t\t// 目标块不存在时忽略处理\n\t\treturn","sourceCodeStart":170,"sourceCodeEnd":206,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/251596fc0de2f9528c00c224252fd073a99973f4/kernel/model/heading.go#L170-L206","documentation":"Thrown by Doc2Heading when the source document (srcTree) is a box-level document — i.e., the top-level root document of a notebook. IsBoxDoc(srcTree.Box, srcTree.ID) returns true when srcTree.ID matches the notebook's (srcTree.Box) designated box document ID. Converting a notebook's top-level document into a heading inside another document would remove the notebook's root entry point, so it is explicitly forbidden. The message is Conf.Language(341) = \"The top-level notebook document cannot be removed or moved\".","triggerScenarios":"Calling POST /api/filetree/doc2Heading with a srcID that is the root document of a notebook (the .sy file directly under the notebook directory that serves as the notebook's main document). This typically happens when a user tries to drag the notebook's top-level document into another document in the file tree.","commonSituations":"User drags the notebook's root document into another document expecting it to merge as headings; an API client selects the box document ID as a source for conversion; confusion about which document is the notebook root vs. a child document.","solutions":["Select a child document (not the notebook's top-level root document) as the source for Doc2Heading — the box document's position is fixed by design.","If you need to move content out of a notebook root document, copy the relevant blocks manually into the target document instead of using Doc2Heading.","As an API client, verify srcID is not the box document by checking IsBoxDoc before calling Doc2Heading."],"exampleFix":"// before\nawait post('/api/filetree/doc2Heading', { srcID: notebookRootDocId, targetID, after: true })\n\n// after — use a child document, never the notebook's top-level root doc\nawait post('/api/filetree/doc2Heading', { srcID: childDocId, targetID, after: true })","handlingStrategy":"validation","validationCode":"// Verify srcID is not a box-level document before calling Doc2Heading\nfunction isBoxDocument(boxID, docID) {\n  // Check against the notebook's root document ID from configuration\n  const notebook = notebooks.find(nb => nb.id === boxID)\n  return notebook && notebook.rootDocID === docID\n}\nif (!isBoxDocument(srcBox, srcID)) {\n  await post('/api/filetree/doc2Heading', { srcID, targetID, after })\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Never use a notebook's top-level root document as the srcID for Doc2Heading.","In the UI, disable drag-to-heading for notebook root documents.","Verify the source document is a child document, not the box doc, before calling the API."],"tags":["heading","doc-conversion","notebook","restriction"],"backgroundTag":null,"analyzedSha":"251596fc0de2f9528c00c224252fd073a99973f4","analyzedAt":"2026-08-12T21:18:37.123Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}