{"record":{"id":"4056a842b8804bfd","repo":"siyuan-note/siyuan","slug":"conf-language-20","errorCode":null,"errorMessage":"Conf.Language(20)","messagePattern":"Conf\\.Language\\(20\\)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/model/heading.go","lineNumber":195,"sourceCode":"\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\n\t}\n\n\ttargetTree, _ := LoadTreeByBlockID(targetID)\n\tif nil == targetTree {\n\t\t// 目标块不存在时忽略处理\n\t\treturn\n\t}","sourceCodeStart":177,"sourceCodeEnd":213,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/8641553a1f07374001902d3ce773285db1292b2d/kernel/model/heading.go#L177-L213","documentation":"Doc2Heading removes the source document's folder after converting it to heading content. If that folder still contains sub-documents (not empty), the conversion would lose child documents, so the kernel aborts with Conf.Language(20) — 'Cannot be converted to heading when including sub-documents'.","triggerScenarios":"Calling Doc2Heading on a document that has nested child documents beneath data/<box>/<docpath>/ on disk.","commonSituations":"Users restructuring notebooks who try to flatten a parent doc that still has children; scripts assuming the doc is a leaf.","solutions":["Move or convert the child documents first, then convert the (now leaf) source document","Use 'move document' instead of doc2Heading when the source has sub-documents","Recursively flatten children into their own headings in multiple calls"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// ensure the source doc has no sub-documents before doc2Heading\nconst children = await getChildDocs(boxID, srcPath);\nif (children.length > 0) throw new Error('move or flatten sub-documents first');","typeGuard":null,"tryCatchPattern":"try {\n  await fetchPost('/api/filetree/doc2Heading', {srcID, targetID});\n} catch (e) {\n  if (String(e.msg).includes('sub-documents')) {\n    // offer user to move children first\n  } else throw e;\n}","preventionTips":["List child documents (via SQL or filetree API) before converting","Prefer move-document for parents with children","Handle the localized message Conf.Language(20) as 'has sub-documents' in clients"],"tags":["filetree","document-structure","precondition"],"backgroundTag":"invalid-state-transition","analyzedSha":"8641553a1f07374001902d3ce773285db1292b2d","analyzedAt":"2026-09-11T16:08:28.414Z","contentChangedAt":"2026-09-11T16:08:28.414Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}