siyuan-note/siyuan · error
box document ID [%s] does not match box ID [%s]
Error message
box document ID [%s] does not match box ID [%s]
What it means
Thrown by readBoxDocID in kernel/model/box_doc.go when boxDoc.json parses and its boxDocID is a well-formed node ID, but it differs from the box ID derived from the notebook folder that contains the file. A notebook's box document is keyed by that notebook's own ID, so a mismatch means the metadata belongs to a different notebook.
Source
Thrown at kernel/model/box_doc.go:80
if err != nil {
if errors.Is(err, os.ErrNotExist) {
err = nil
}
return
}
meta := &boxDocMeta{}
if err = gulu.JSON.UnmarshalJSON(data, meta); err != nil {
return "", fmt.Errorf("unmarshal box document metadata failed: %w", err)
}
if boxDocMetaSpec != meta.Spec {
return "", fmt.Errorf("unsupported box document metadata spec [%d]", meta.Spec)
}
if !ast.IsNodeIDPattern(meta.BoxDocID) {
return "", fmt.Errorf("invalid box document ID [%s]", meta.BoxDocID)
}
if boxID != meta.BoxDocID {
return "", fmt.Errorf("box document ID [%s] does not match box ID [%s]", meta.BoxDocID, boxID)
}
return boxID, nil
}
func writeBoxDocID(boxID string) error {
meta := &boxDocMeta{Spec: boxDocMetaSpec, BoxDocID: boxID}
data, err := gulu.JSON.MarshalIndentJSON(meta, "", " ")
if err != nil {
return fmt.Errorf("marshal box document metadata failed: %w", err)
}
return filelock.WriteFile(boxDocMetaPath(boxID), data)
}
func IsBoxDocEnabled() bool {
return nil != Conf && nil != Conf.FileTree && nil != Conf.FileTree.BoxDocEnabled && *Conf.FileTree.BoxDocEnabled
}
func hiddenBoxDocRootIDs() (ret []string) {View on GitHub (pinned to afa823b6b4)
Solutions
- Read the two IDs from the error message: the first is the stored boxDocID, the second is the notebook folder name
- Either rename the data folder back to the stored boxDocID, or edit boxDoc.json so boxDocID equals the current folder name
- If unsure which is authoritative, delete boxDoc.json and let EnsureBoxDoc regenerate it from the existing hidden /<boxID>.sy document
- Re-run the failed mount/sync operation
Example fix
// before: data/20240601120000-newbox01/.siyuan/boxDoc.json
{"spec":1,"boxDocID":"20230101130405-oldbox1"}
// after
{"spec":1,"boxDocID":"20240601120000-newbox01"} Defensive patterns
Strategy: try-catch
Validate before calling
// read the meta and compare before triggering the failing path
data, err := os.ReadFile(filepath.Join(dataDir, boxID, ".siyuan", "boxDoc.json"))
if err == nil {
var m struct {
Spec int `json:"spec"`
BoxDocID string `json:"boxDocID"`
}
_ = json.Unmarshal(data, &m)
if m.BoxDocID != "" && m.BoxDocID != boxID {
// fix or delete the file before calling EnsureBoxDoc
}
} Try / catch
if _, err := model.EnsureBoxDoc(boxID); err != nil {
if strings.Contains(err.Error(), "does not match box ID") {
// metadata belongs to another notebook: rename folder back or delete boxDoc.json
}
return err
} Prevention
- Rename/duplicate notebooks only through SiYuan APIs so boxDoc.json follows
- Never copy .siyuan metadata between notebook folders manually
- After workspace restores, open every notebook once and check kernel logs
When it happens
Trigger: Renaming a notebook's data folder without regenerating boxDoc.json; copying a notebook directory to a new folder name; restoring another workspace's .siyuan metadata into this one; sync applying a boxDoc.json from a snapshot of a differently-IDed notebook.
Common situations: Manual workspace surgery or migration; backup/restore into a different workspace; mixed-version sync where one peer wrote meta under an old box ID; copying a notebook via the file manager instead of SiYuan's own duplicate-notebook API.
Related errors
- invalid box document ID [%s]
- invalid box ID [%s]
- box document ID [%s] is already in use
- 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/7f81d3821e33e53a.
Report an issue: GitHub.