siyuan-note/siyuan · error
box document ID [ ] does not match box ID [ ]
Error message
box document ID [%s] does not match box ID [%s]
What it means
The boxDocID stored in boxDoc.json must equal the notebook (box) ID, because the box doc node is the notebook's own root document. A mismatch means the metadata file belongs to a different notebook — likely copied, renamed, or stale — and readBoxDocID refuses to return an inconsistent identity.
Solutions
- Delete the stale .siyuan/boxDoc.json in the notebook so ensureBoxDoc0 rewrites it with the current box ID
- Edit boxDoc.json so boxDocID matches the notebook's directory ID (if intentional)
- When duplicating notebooks, use SiYuan's copy/import flow rather than copying raw data directories
Example fix
// before: copied from notebook 20240101120000-oldid01
{"spec": 1, "boxDocID": "20240101120000-oldid01"}
// after
{"spec": 1, "boxDocID": "20240101120000-newid01"} Defensive patterns
Strategy: try-catch
Validate before calling
var probe struct{ BoxDocID string `json:"boxDocID"` }
if json.Unmarshal(data, &probe) == nil && probe.BoxDocID != boxID {
// metadata from another notebook: delete and regenerate
} Type guard
func matchesBox(data []byte, boxID string) bool {
var m struct{ BoxDocID string `json:"boxDocID"` }
return json.Unmarshal(data, &m) == nil && m.BoxDocID == boxID
} Try / catch
if _, err := readBoxDocID(boxID); err != nil && strings.Contains(err.Error(), "does not match box ID") {
// stale/copied metadata: remove .siyuan/boxDoc.json and let ensureBoxDoc0 rewrite it
} Prevention
- Duplicate notebooks via SiYuan's copy/import, not directory copies
- Rename notebooks through the app, not the filesystem
- After restoring from backup, confirm boxDoc.json matches the notebook folder ID
When it happens
Trigger: readBoxDocID (via ensureBoxDoc0 or TestBoxDocMetadataMatchesBoxID) finds meta.BoxDocID != boxID — e.g. the whole .siyuan directory was copied between notebooks, or the notebook folder was renamed without regenerating boxDoc.json.
Common situations: Duplicating a notebook by copying its data directory (boxDoc.json still names the old ID); renaming the notebook folder manually; sync restoring a boxDoc.json from another notebook.
Understand the failure class
Background: "invalid id" errors: invalid identifier format — why libraries reject IDs before lookup, and how to fix them — this error's family across 37 libraries.
Related errors
AI-assisted analysis of siyuan-note/siyuan@9f775e8a12 (2026-09-19).
Data as JSON: /api/errors/7f81d3821e33e53a.
Report an issue: GitHub.
Appendix: 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 9f775e8a12)