siyuan-note/siyuan · error
unmarshal box document metadata failed: %w
Error message
unmarshal box document metadata failed: %w
What it means
readBoxDocID parses <workspace>/data/<notebookID>/.siyuan/boxDoc.json, which binds the notebook to its box document (spec + boxDocID, kernel/model/box_doc.go:39-47). If the file exists but gulu.JSON cannot unmarshal it, the parse error is wrapped with this message. A missing file is fine (returns nil error); only malformed content fails.
Source
Thrown at kernel/model/box_doc.go:71
func boxDocPath(boxID string) string {
if "" == boxID {
return ""
}
return "/" + boxID + ".sy"
}
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)View on GitHub (pinned to afa823b6b4)
Solutions
- Restore data/<boxID>/.siyuan/boxDoc.json from backup/history
- If no backup exists, delete the corrupted boxDoc.json — readBoxDocID tolerates absence and the kernel rewrites it (spec 1, boxDocID = box ID) on next notebook write
- Exclude the workspace from sync-agent partial writes, or pause sync during kernel shutdown
Defensive patterns
Strategy: try-catch
Try / catch
try { readBoxDocID(boxID) } catch (e) { if (e.Contains("unmarshal box document metadata")) { restoreOrDeleteBoxDocJson(boxID); /* kernel regenerates it */ } } Prevention
- Let the kernel shut down cleanly so boxDoc.json is never truncated
- Pause sync agents during kernel shutdown
- Restore .siyuan metadata from backup rather than hand-fixing JSON
When it happens
Trigger: Any notebook open/mount path that calls readBoxDocID when boxDoc.json is truncated or corrupt: partial write after a crash, external editor saving invalid JSON, disk or sync corruption.
Common situations: Power loss mid-write; sync clients (Dropbox/OneDrive) producing conflict-truncated files; hand-editing the .siyuan metadata directory.
Understand the failure class
Background: JSON parse error: "Unexpected token" / "not valid JSON" / "failed to parse" — what JSON parsers are really complaining about — this error's family across 45 libraries.
Related errors
- invalid session id
- path [%s] must not contain '..'
- path [%s] escapes box directory
- parse json [%s] to tree failed: %w
- unmarshal AI editor actions failed: %w
AI-assisted analysis of siyuan-note/siyuan@afa823b6b4 (2026-08-18).
Data as JSON: /api/errors/4db7218a0fb0ffbc.
Report an issue: GitHub.