siyuan-note/siyuan · error
target document [ ] is not in an opened and unlocked…
Error message
target document [%s] is not in an opened and unlocked notebook
What it means
After locating the target document, the kernel looks up its notebook with Conf.Box. If the notebook is nil — meaning it is closed, not currently open in the workspace, or unknown — the reorder is refused because documents in closed/unlocked-away notebooks must not be mutated.
Solutions
- Open the target document's notebook in the SiYuan UI (or add it back to opened notebooks) before reordering
- Confirm the notebook ID in the request matches an opened notebook via the notebooks listing API
- Re-resolve target.BoxID from a fresh GetBlockTree lookup instead of a cached value
- Remove or skip the operation for documents in closed notebooks in your automation logic
Example fix
// before
reorderDocs(srcIDs, targetID, "after") // target notebook may be closed
// after
if isNotebookOpen(notebookIDOf(targetID)) {
reorderDocs(srcIDs, targetID, "after")
} Defensive patterns
Strategy: validation
Validate before calling
const nb = await getNotebook(target.notebookId)
if (!nb || nb.closed) throw new Error("open the notebook before reordering") Type guard
const isOpenNotebook = (nb) => !!nb && nb.closed === false
Try / catch
try { await api.reorderDocs(src, targetID, pos) } catch (e) { if (String(e).includes("opened and unlocked notebook")) { await openNotebook(target.notebookId); return retryOnce() } throw e } Prevention
- Check the notebooks listing for closed state before mutating documents
- Skip closed notebooks explicitly in automation job filters
- Re-open notebooks programmatically when operating across workspace switches
When it happens
Trigger: Reordering documents whose target lives in a notebook that was closed by the user; the notebook ID was mistyped so Conf.Box finds no matching opened notebook; workspace was switched and the notebook is no longer loaded.
Common situations: Automations running against notebooks the user closed; sync tooling operating on notebooks outside the opened set; stale references after removing a notebook from the workspace.
Understand the failure class
Background: Permission denied / not authorized / 403 Forbidden: access-control rejections when the caller lacks the required role, grant, or ownership — this error's family across 18 libraries.
Related errors
- ErrBoxClosed
- ErrBoxNotFound
- 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/8eeddeea6e85abc5.
Report an issue: GitHub.
Appendix: source
Thrown at kernel/model/file.go:2692
Notebook string `json:"notebook,omitempty"`
ParentPath string `json:"parentPath,omitempty"`
}
// ReorderDocs 将同级文档移动到目标文档之前或之后。
func ReorderDocs(sourceIDs []string, targetID, position string) (ret *ReorderResult, err error) {
ret = &ReorderResult{}
if err = validateReorderArgs(sourceIDs, targetID, position); nil != err {
return
}
FlushTxQueue()
target := treenode.GetBlockTree(targetID)
if !isSortableDocument(target) {
return ret, fmt.Errorf("target document [%s] not found", targetID)
}
box := Conf.Box(target.BoxID)
if nil == box {
return ret, fmt.Errorf("target document [%s] is not in an opened and unlocked notebook", targetID)
}
parentPath := path.Dir(target.Path)
for _, sourceID := range sourceIDs {
source := treenode.GetBlockTree(sourceID)
if !isSortableDocument(source) {
return ret, fmt.Errorf("source document [%s] not found", sourceID)
}
if source.BoxID != target.BoxID || path.Dir(source.Path) != parentPath {
return ret, fmt.Errorf("source document [%s] is not a sibling of target document [%s]", sourceID, targetID)
}
}
fileTreeSortLock.Lock()
confPath := filepath.Join(util.DataDir, box.ID, ".siyuan", "sort.json")
fullSortIDs, readErr := readSortConfMap(confPath)
if nil != readErr {
fileTreeSortLock.Unlock()
return ret, readErrView on GitHub (pinned to 9f775e8a12)