siyuan-note/siyuan · error
invalid box document ID
Error message
invalid box document ID [%s]
What it means
The boxDocID field inside boxDoc.json must be a valid SiYuan node ID (ast.IsNodeIDPattern: timestamp-suffix format). This error means the stored ID is malformed — empty string, wrong characters, or truncated — so the notebook's document-node identity cannot be trusted.
Solutions
- Fix boxDocID in .siyuan/boxDoc.json to the notebook's actual node ID (timestamp-randomid format)
- Simplest: delete .siyuan/boxDoc.json and let SiYuan regenerate it via writeBoxDocID with the correct box ID
- If the notebook's root document .sy file was renamed/re-IDed, align boxDoc.json with the new ID or recreate the notebook doc node
Example fix
// before
{"spec": 1, "boxDocID": "mynotebook"}
// after
{"spec": 1, "boxDocID": "20240101120000-abcdefg"} Defensive patterns
Strategy: validation
Validate before calling
var probe struct{ BoxDocID string `json:"boxDocID"` }
if json.Unmarshal(data, &probe) == nil && !ast.IsNodeIDPattern(probe.BoxDocID) {
// invalid ID: delete boxDoc.json so it is rewritten with the box ID
} Type guard
func validBoxDocID(id string) bool { return ast.IsNodeIDPattern(id) } Try / catch
if _, err := readBoxDocID(boxID); err != nil && strings.Contains(err.Error(), "invalid box document ID") {
// regenerate boxDoc.json via writeBoxDocID
} Prevention
- Treat boxDoc.json as generated output — regenerate, don't repair
- Only use kernel-produced node IDs
- Validate after manual notebook directory manipulation
When it happens
Trigger: readBoxDocID parses a boxDoc.json whose boxDocID fails ast.IsNodeIDPattern, e.g. {"spec":1,"boxDocID":""} or a hand-typed/nonstandard identifier.
Common situations: Manual editing of boxDoc.json with an invented ID; a tool writing the field as a non-ID string; an older/buggy writer producing an empty boxDocID.
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
- box document ID [ ] does not match box ID [ ]
- invalid box ID [ ]
- invalid box ID [ ]
- invalid notebook ID
- [ ] is not a box id
AI-assisted analysis of siyuan-note/siyuan@9f775e8a12 (2026-09-19).
Data as JSON: /api/errors/158ad0520630287f.
Report an issue: GitHub.
Appendix: 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 9f775e8a12)