siyuan-note/siyuan · error

cannot move document [%s] into itself

Error message

cannot move document [%s] into itself

What it means

ReorderDocTree refuses a drag-and-drop reorder when a source document's path contains (or equals) the target document's path, i.e. the user is trying to move a document into one of its own descendants or onto itself. Since custom ordering is stored per parent folder, nesting a document under itself would corrupt the document tree, so the operation is rejected up front.

Source

Thrown at kernel/model/file_tree_reorder.go:156

	}
	siblingIDs, err := loadSiblingCustomOrder(box.ID, parentDir, existingSorts)
	if err != nil {
		return nil, err
	}
	for _, id := range siblingIDs {
		if byID[id] == nil {
			return nil, fmt.Errorf("document [%s] could not be read", id)
		}
	}
	var fromPaths []string
	for _, id := range sourceIDs {
		source := treenode.GetBlockTree(id)
		if !isSortableDocument(source) || Conf.Box(source.BoxID) == nil {
			return nil, fmt.Errorf("source document [%s] is unavailable", id)
		}
		if source.BoxID == target.BoxID && (target.Path == source.Path ||
			strings.HasPrefix(target.Path, strings.TrimSuffix(source.Path, ".sy")+"/")) {
			return nil, fmt.Errorf("cannot move document [%s] into itself", id)
		}
		if byID[id] != nil {
			continue
		}
		sourceParent := path.Dir(source.Path)
		if sourceParent != "/" {
			sourceParent += ".sy"
		}
		sourceDocs, _, loadErr := ListDocTree(source.BoxID, sourceParent, util.SortModeCustom, false, true, int(^uint(0)>>1))
		if loadErr != nil {
			return nil, loadErr
		}
		for _, doc := range sourceDocs {
			if doc.ID == id {
				byID[id] = doc
				break
			}
		}

View on GitHub (pinned to 8641553a1f)

Solutions

  1. Pick a different target document that is not a descendant of (or equal to) any source document.
  2. Before calling the API, compare paths: reject when target path equals the source path or starts with strings.TrimSuffix(source.Path, ".sy") + "/" in the same notebook.
  3. Refresh document IDs/paths from the current tree if stale references caused the bad target.

Example fix

// before
await reorderDocTree(boxID, [parentID], childID, position)
// after
if (targetPath === srcPath || targetPath.startsWith(srcPath.replace(/\.sy$/, '') + '/')) {
  throw new Error('cannot move a document into its own subtree')
}
await reorderDocTree(boxID, [parentID], siblingID, position)
Defensive patterns

Strategy: validation

Validate before calling

function isDescendantOrSelf(srcPath, targetPath) {
  const base = srcPath.replace(/\.sy$/, '')
  return targetPath === srcPath || targetPath.startsWith(base + '/')
}
// call the API only if !isDescendantOrSelf(source.path, target.path) and same notebook

Try / catch

try {
  await reorderDocTree(boxID, sourceIDs, targetID, position)
} catch (e) {
  if (String(e.msg).includes('into itself')) showMsg('Cannot move a document into its own subtree')
  else throw e
}

Prevention

When it happens

Trigger: Calling ReorderDocTree (via the reorderDocs API) with a targetID whose .sy path equals the source document's path, or whose path has the source's path-minus-.sy plus "/" as a prefix, in the same notebook. In practice: dropping a document onto one of its own child documents or onto itself.

Common situations: A developer or script automating document reordering passes the wrong target ID; a UI bug lets users drop a parent document onto its own subtree; stale IDs after a rename/removal make the target resolve to a descendant of the source.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


AI-assisted analysis of siyuan-note/siyuan@8641553a1f (2026-09-11). Data as JSON: /api/errors/085c9c42152f7639. Report an issue: GitHub.