siyuan-note/siyuan · error

Cannot be converted to heading when including sub-documents

Error message

Cannot be converted to heading when including sub-documents

What it means

Thrown by Doc2Heading when the source document has a subdirectory on disk (subDir) that is not empty. The subdirectory represents child documents of the source document. Converting a parent document with children into a heading inside another document would orphan those child documents because their parent .sy file would be consumed. The check uses util.IsEmptyDir(subDir) — if the directory exists but is non-empty, the error fires. The message is Conf.Language(20) = "Cannot be converted to heading when including sub-documents".

Source

Thrown at kernel/model/heading.go:195

		return
	}

	FlushTxQueue()

	srcTree, _ := LoadTreeByBlockID(srcID)
	if nil == srcTree {
		err = ErrBlockNotFound
		return
	}
	if IsBoxDoc(srcTree.Box, srcTree.ID) {
		err = errors.New(Conf.Language(341))
		return
	}

	subDir := filepath.Join(util.DataDir, srcTree.Box, strings.TrimSuffix(srcTree.Path, ".sy"))
	if gulu.File.IsDir(subDir) {
		if !util.IsEmptyDir(subDir) {
			err = errors.New(Conf.Language(20))
			return
		}

		if removeErr := os.Remove(subDir); nil != removeErr { // 移除空文件夹不会有副作用
			logging.LogWarnf("remove empty dir [%s] failed: %s", subDir, removeErr)
		}
	}

	if nil == treenode.GetBlockTree(targetID) {
		// 目标块不存在时忽略处理
		return
	}

	targetTree, _ := LoadTreeByBlockID(targetID)
	if nil == targetTree {
		// 目标块不存在时忽略处理
		return
	}

View on GitHub (pinned to 251596fc0d)

Solutions

  1. Move or delete all child documents under the source document first, so its subdirectory becomes empty (or is removed), then retry Doc2Heading.
  2. Alternatively, convert each child document individually rather than converting the parent document that contains them.
  3. As an API client, check for the existence of child documents before calling Doc2Heading on a parent document.
Defensive patterns

Strategy: validation

Validate before calling

// Check for child documents before calling Doc2Heading
async function hasChildDocuments(srcID) {
  // Check if the document has a non-empty subdirectory on disk
  const children = await get('/api/filetree/listDocsByPath', { notebook: srcBox, path: srcID })
  return children && children.files && children.files.length > 0
}
if (!(await hasChildDocuments(srcID))) {
  await post('/api/filetree/doc2Heading', { srcID, targetID, after })
}

Prevention

When it happens

Trigger: Calling POST /api/filetree/doc2Heading with a srcID whose corresponding .sy file has a sibling directory (named after the document ID with .sy stripped) containing child .sy documents. This happens when a user tries to convert a document that has sub-documents (a parent in the document hierarchy) into a heading.

Common situations: User has a document with child documents (created via heading-level nesting or block embeds) and tries to drag it into another document to merge as headings; an API client selects a parent document without checking for children.

Related errors


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