siyuan-note/siyuan · error

box document ID [%s] is already in use

Error message

box document ID [%s] is already in use

What it means

When the box-doc feature is enabled and a notebook has no box document yet, ensureBoxDoc0 (kernel/model/box_doc.go:151) tries to create the hidden doc at /<boxID>.sy with root ID equal to the box ID. Creation is refused if that file already exists on disk, a block tree with that ID is already indexed (treenode.GetBlockTree), or any unindexed tree in any notebook carries that root ID (findUnindexedTreePathInAllBoxes). Block IDs are globally unique in a workspace, so the box doc ID cannot collide with real content.

Source

Thrown at kernel/model/box_doc.go:151

	box := Conf.GetBox(boxID)
	if nil == box {
		return "", ErrBoxNotFound
	}

	if !IsBoxDocEnabled() {
		return
	}
	boxDocID = boxID

	boxDocID, err = findBoxDoc(box)
	if err != nil {
		return "", err
	}
	created, changed := false, false
	if "" == boxDocID {
		boxDocID = boxID
		if box.Exist(boxDocPath(boxDocID)) || nil != treenode.GetBlockTree(boxDocID) || "" != findUnindexedTreePathInAllBoxes(boxDocID) {
			return "", fmt.Errorf("box document ID [%s] is already in use", boxDocID)
		}
		if err = createBoxDoc(box, boxDocID); err != nil {
			return "", err
		}
		created = true
		changed = true
	} else {
		indexBoxDocIfNeeded(boxID, boxDocID)
		if err = reconcileBoxDoc(box, boxDocID); err != nil {
			return "", err
		}
	}

	storedBoxDocID, _ := readBoxDocID(boxID)
	if storedBoxDocID != boxID {
		if err = writeBoxDocID(boxID); err != nil {
			return "", err
		}

View on GitHub (pinned to afa823b6b4)

Solutions

  1. Run an index rebuild (or /api/filetree/refreshFiletree) to clear stale index entries, then retry
  2. Search the colliding ID via SQL (select * from blocks where id = '<boxID>') to locate the occupying document
  3. If the occupying doc is junk or a leftover, delete it (and its /<boxID>.sy file), then retry
  4. If it is real content, change that block's ID or move the content, then re-enable the box doc feature

Example fix

// SQL to locate the colliding block before retrying EnsureBoxDoc
SELECT id, box, path, content FROM blocks WHERE id = '20230101130405-oldbox1';
// after resolving (delete/rename), the same call succeeds
boxDocID, err := model.EnsureBoxDoc(boxID)
Defensive patterns

Strategy: try-catch

Validate before calling

// pre-flight the three occupation checks the kernel performs
occupied := box.Exist("/" + boxID + ".sy") ||
	treenode.GetBlockTree(boxID) != nil ||
	findUnindexedTreePathInAllBoxes(boxID) != ""
if occupied {
	// resolve the collision before enabling the box-doc feature
}

Try / catch

if _, err := model.EnsureBoxDoc(boxID); err != nil {
	if strings.Contains(err.Error(), "already in use") {
		// rebuild index; locate and move/re-ID the colliding doc; retry once
	}
	return err
}

Prevention

When it happens

Trigger: Enabling the box-doc feature on a workspace where some document's ID happens to equal a notebook ID (typical after import, migration, or manual .sy tampering); a leftover partial /<boxID>.sy file; the index still holding a stale entry for that ID.

Common situations: Feature toggled on after the workspace existed for a long time; data imported from another workspace preserving IDs; an earlier box-doc creation interrupted halfway, leaving the .sy file but no boxDoc.json; index not rebuilt after manual file operations.

Related errors


AI-assisted analysis of siyuan-note/siyuan@afa823b6b4 (2026-08-18). Data as JSON: /api/errors/1948abdb2988057e. Report an issue: GitHub.