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
- Run an index rebuild (or /api/filetree/refreshFiletree) to clear stale index entries, then retry
- Search the colliding ID via SQL (select * from blocks where id = '<boxID>') to locate the occupying document
- If the occupying doc is junk or a leftover, delete it (and its /<boxID>.sy file), then retry
- 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
- Rebuild the index after bulk file operations before enabling the box-doc feature
- Avoid importing raw .sy trees that preserve IDs clashing with notebook IDs
- When enabling the feature workspace-wide, refreshFiletree first
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
- invalid box document ID [%s]
- box document ID [%s] does not match box ID [%s]
- invalid box ID [%s]
- path belongs to encrypted notebook [%s]
- encrypted notebook is locked, please unlock it first
AI-assisted analysis of siyuan-note/siyuan@afa823b6b4 (2026-08-18).
Data as JSON: /api/errors/1948abdb2988057e.
Report an issue: GitHub.