siyuan-note/siyuan · error
invalid box document ID [%s]
Error message
invalid box document ID [%s]
What it means
Thrown by readBoxDocID in kernel/model/box_doc.go when parsing data/<boxID>/.siyuan/boxDoc.json, the metadata file that registers a notebook's hidden 'box document'. The boxDocID field fails ast.IsNodeIDPattern, i.e. it is not a valid SiYuan node ID (14-digit timestamp, dash, 7-char suffix, e.g. 20240102150405-a1b2c3d). The file is treated as untrusted on-disk state, so any malformed value aborts box-doc initialization for that notebook.
Source
Thrown at kernel/model/box_doc.go:77
func readBoxDocID(boxID string) (ret string, err error) {
data, err := filelock.ReadFile(boxDocMetaPath(boxID))
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.BoxDocEnabledView on GitHub (pinned to afa823b6b4)
Solutions
- Open <workspace>/data/<boxID>/.siyuan/boxDoc.json and inspect the boxDocID value; the filename <boxID> is the expected value
- Set the file to {"spec":1,"boxDocID":"<boxID>"} where <boxID> is the notebook folder name, provided it matches the node-ID pattern
- If the meta is unrecoverable, delete boxDoc.json (the kernel regenerates it on the next EnsureBoxDoc, reusing the existing hidden doc) and re-run the mount/open operation
- Check kernel logs for 'initialized box document' to confirm recovery
Example fix
// before: data/20230102150405-a1b2c3d/.siyuan/boxDoc.json
{"spec":1,"boxDocID":""}
// after
{"spec":1,"boxDocID":"20230102150405-a1b2c3d"} Defensive patterns
Strategy: try-catch
Try / catch
boxDocID, err := model.EnsureBoxDoc(boxID)
if err != nil {
if strings.Contains(err.Error(), "invalid box document ID") {
log.Printf("boxDoc.json for %s is malformed; regenerate it", boxID)
}
return err
} Prevention
- Do not hand-edit boxDoc.json; let the kernel own it
- Exclude data/*/.siyuan/ from external JSON formatters and linters
- Validate workspace backups by mounting each notebook after restore
- Keep regular kernel-version-consistent backups so metadata format stays native
When it happens
Trigger: Calling EnsureBoxDoc (notebook mount at kernel/model/mount.go:475, sync apply at kernel/model/repository.go:2509, or feature refresh) for a notebook whose boxDoc.json was hand-edited, truncated by a partial write, corrupted by an interrupted sync, or written by a different spec version. Empty or garbage boxDocID values reproduce it directly.
Common situations: Workspace restored from an incomplete backup; sync-conflict artifacts inside the notebook's .siyuan folder; external tools that rewrite/pretty-print the JSON and blank the field; manually copying a notebook folder and hand-editing only conf.json.
Related errors
- box document ID [%s] does not match box ID [%s]
- invalid box ID [%s]
- box document ID [%s] is already in use
- The top-level notebook document cannot be removed or moved
- notebook [%s] not found
AI-assisted analysis of siyuan-note/siyuan@afa823b6b4 (2026-08-18).
Data as JSON: /api/errors/158ad0520630287f.
Report an issue: GitHub.