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

  1. Open the target document's notebook in the SiYuan UI (or add it back to opened notebooks) before reordering
  2. Confirm the notebook ID in the request matches an opened notebook via the notebooks listing API
  3. Re-resolve target.BoxID from a fresh GetBlockTree lookup instead of a cached value
  4. 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

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


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, readErr

View on GitHub (pinned to 9f775e8a12)