siyuan-note/siyuan · error
ErrBoxClosed
ErrBoxClosed
Error message
notebook closed
What it means
ErrBoxClosed is the sentinel error indicating the target notebook exists but is closed; most write and index operations refuse to touch closed notebooks. Callers such as the CLI formatNotebookWriteError translate it into a hint telling the user to run `notebook open --id <boxID>` first.
Solutions
- Open the notebook first: POST /api/notebook/openNotebook {"notebook": "<boxID>"} or run `siyuan-kernel notebook open --id <boxID>`
- Check notebook state with lsNotebooks before writing and open it if closed
- In Go callers, branch on errors.Is(err, model.ErrBoxClosed) to give the user an actionable message
- Ensure workspace boot/open sequence completes before scripts that touch the notebook run
Example fix
// before
err := writeDoc(boxID, path, data)
if err != nil { return err }
// after
if err != nil {
if errors.Is(err, model.ErrBoxClosed) {
return fmt.Errorf("notebook [%s] is closed; run `notebook open --id %s` first", boxID, boxID)
}
return err
} Defensive patterns
Strategy: validation
Validate before calling
// TypeScript: check notebook state and open if closed before writing
const conf = await fetchPost('/api/notebook/lsNotebooks', {});
const nb = conf.data.notebooks.find(n => n.id === boxID);
if (nb && nb.closed) await fetchPost('/api/notebook/openNotebook', { notebook: boxID }); Type guard
function isBoxClosed(err: unknown): err is Error {
return err instanceof Error && err.message === 'notebook closed';
} Try / catch
// Go
if errors.Is(err, model.ErrBoxClosed) {
return fmt.Errorf("notebook [%s] is closed; run `notebook open --id %s` first", boxID, boxID)
} Prevention
- Open the notebook programmatically before headless/CLI write operations
- Check the notebook's closed flag in lsNotebooks output before automation runs
- Sequence scripts after workspace boot completes so target notebooks are open
When it happens
Trigger: Writing to or reading docs of a notebook whose .si/config was closed via /api/notebook/closeNotebook (or the UI); the CLI notebook write/import paths hit it and formatNotebookWriteError rewraps it with the open command; getOpenedBox rejects a closed box.
Common situations: User closed the notebook in the UI while automation/CLI scripts still target it; headless scripts operate on notebooks that default to closed after workspace restore; a plugin runs before the notebook is opened at boot.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- ErrBoxNotFound
- target document [ ] is not in an opened and unlocked…
- block [ ] is not a document
- block [ ] is not a document that can declare a child…
- block [ ] is not a sortable document
AI-assisted analysis of siyuan-note/siyuan@9f775e8a12 (2026-09-19).
Data as JSON: /api/errors/4ea9007d856a5517.
Report an issue: GitHub.
Appendix: source
Thrown at kernel/model/tree.go:211
logging.LogErrorf("decrypt tree [path=%s] failed: %s", localPath, err)
return
}
}
if err = treenode.CheckSpecJSON(data); nil != err {
return
}
ret, err = dataparser.ParseJSONWithoutFix(data, luteEngine.ParseOptions)
if err != nil {
logging.LogErrorf("parse json to tree [%s] failed: %s", localPath, err)
return
}
return
}
var (
ErrBoxNotFound = errors.New("notebook not found")
ErrBoxClosed = errors.New("notebook closed")
ErrBlockNotFound = errors.New("block not found")
ErrTreeNotFound = errors.New("tree not found")
ErrIndexing = errors.New("indexing")
ErrBoxUnindexed = errors.New("notebook unindexed")
ErrInvalidID = errors.New("invalid id")
)
func LoadTreeByBlockIDWithReindex(id string) (ret *parse.Tree, err error) {
return LoadTreeByBlockIDWithReindexInBox(id, "")
}
// LoadTreeByBlockIDWithReindexInBox 与 LoadTreeByBlockIDWithReindex 一致,但按 boxID 路由 blocktree 查询。
func LoadTreeByBlockIDWithReindexInBox(id, boxID string) (ret *parse.Tree, err error) {
if "" == id {
logging.LogWarnf("block id is empty")
return nil, ErrTreeNotFound
}
View on GitHub (pinned to 9f775e8a12)