siyuan-note/siyuan · error
invalid box ID [ ]
Error message
invalid box ID [%s]
What it means
saveConf0 validates that the box ID is a syntactically valid SiYuan node ID (ast.IsNodeIDPattern, a 14-char lowercase alnum string) before writing conf.json. This error means the Box struct carries an ID that is not a valid notebook identifier, so the write is refused to avoid creating garbage directories under DataDir.
Solutions
- Ensure the Box was created via a valid notebook creation path so the ID matches the node-ID pattern (14 lowercase alphanumerics)
- Check the directory name under the workspace data/ folder — it must be a valid node ID
- If constructing a Box in tests, use a generated valid ID (e.g. via the ID generator) instead of a literal
- Guard callers: verify ast.IsNodeIDPattern(box.ID) before calling SaveConf
Example fix
// before
box := &Box{ID: "my-notebook"}
err := box.SaveConf(conf)
// after
if !ast.IsNodeIDPattern(box.ID) {
return fmt.Errorf("refusing to save conf: invalid box ID %q", box.ID)
}
err := box.SaveConf(conf) Defensive patterns
Strategy: validation
Validate before calling
// Go: validate the box ID before saving
if !ast.IsNodeIDPattern(box.ID) {
return fmt.Errorf("invalid notebook ID %q", box.ID)
} Try / catch
if err := box.SaveConf(conf); err != nil {
if strings.Contains(err.Error(), "invalid box ID") {
// recreate Box from a valid notebook path
}
} Prevention
- Never construct Box structs with hand-made IDs
- Load boxes via the standard listing/loading API
- In tests, generate IDs with the node-ID generator
When it happens
Trigger: Calling Box.SaveConf on a Box whose ID was constructed programmatically (not loaded from disk), e.g. an empty ID, a path fragment, or an ID with invalid characters. Raised by saveConf0 which is invoked from SaveConf whenever conf.json must be written.
Common situations: Tests constructing Box{ID: "test"} directly; plugins or scripts passing fabricated notebook IDs; corrupted in-memory state after a failed box load; migrating data with legacy/renamed notebook folder names.
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/52ad5ee8ee84137a.
Report an issue: GitHub.
Appendix: source
Thrown at kernel/model/box.go:375
if err = box.saveConf0(newData); err != nil {
return err
}
return syncBoxConfCryptoBackup(box.ID, persisted)
}
func syncBoxConfCryptoBackup(boxID string, boxConf *conf.BoxConf) error {
if !boxConf.Encrypted || boxConf.BoxCrypt == nil {
return nil
}
if needWriteNotebookCryptBackup(boxID, boxConf.BoxCrypt) {
return writeNotebookCryptBackup(boxID, boxConf.BoxCrypt)
}
return nil
}
func (box *Box) saveConf0(data []byte) error {
if !ast.IsNodeIDPattern(box.ID) {
return fmt.Errorf("invalid box ID [%s]", box.ID)
}
confPath := filepath.Join(util.DataDir, box.ID, ".siyuan/conf.json")
if err := os.MkdirAll(filepath.Join(util.DataDir, box.ID, ".siyuan"), 0755); err != nil {
return fmt.Errorf("mkdir box conf dir failed: %w", err)
}
if err := filelock.WriteFile(confPath, data); err != nil {
util.ReportFileSysFatalError(err)
return fmt.Errorf("write box conf [%s] failed: %w", confPath, err)
}
invalidateEncryptedPublishAccessCache()
return nil
}
// validateBoxPath 校验 box 内相对路径,拒绝 .. 和绝对路径,确保最终路径在 <DataDir>/<boxID>/ 内。
func (box *Box) validateBoxPath(p string) (string, error) {
return filesys.ValidateBoxRelativePath(box.ID, p)
}View on GitHub (pinned to 9f775e8a12)