siyuan-note/siyuan · error

invalid box ID [%s]

Error message

invalid box ID [%s]

What it means

First guard in ensureBoxDoc0 (kernel/model/box_doc.go): the boxID argument must satisfy ast.IsNodeIDPattern before anything else runs. Callers are notebook mount (kernel/model/mount.go:475), config-driven ensure (kernel/model/conf.go:1311) and repo sync apply (kernel/model/repository.go:2509). The error signals a malformed notebook identifier in persisted state or a bad API argument, not a runtime failure.

Source

Thrown at kernel/model/box_doc.go:130

	var bt *treenode.BlockTree
	if "" == boxID {
		bt = treenode.GetBlockTree(id)
	} else {
		bt = treenode.GetBlockTreeInBox(id, boxID)
	}
	return nil != bt && IsBoxDoc(bt.BoxID, bt.RootID)
}

func EnsureBoxDoc(boxID string) (boxDocID string, err error) {
	createDocLock.Lock()
	defer createDocLock.Unlock()
	return ensureBoxDoc0(boxID)
}

// ensureBoxDoc0 的调用方必须持有 createDocLock。
func ensureBoxDoc0(boxID string) (boxDocID string, err error) {
	if !ast.IsNodeIDPattern(boxID) {
		return "", fmt.Errorf("invalid box ID [%s]", boxID)
	}

	box := Conf.GetBox(boxID)
	if nil == box {
		return "", ErrBoxNotFound
	}

	if !IsBoxDocEnabled() {
		return
	}
	boxDocID = boxID

	boxDocID, err = findBoxDoc(box)
	if err != nil {
		return "", err
	}
	created, changed := false, false
	if "" == boxDocID {

View on GitHub (pinned to afa823b6b4)

Solutions

  1. Verify the notebook ID passed in: it must match ^\d{14}-[a-z0-9]{7}$
  2. If a hand-created folder caused it, rename it to a valid unique node ID or remove it and create the notebook through SiYuan's UI/API
  3. If conf.json lists a malformed notebook, remove that entry and restart the kernel
  4. For repo sync, inspect the incoming snapshot's notebook IDs before applying

Example fix

// before
boxID := "my-notebook"
ensureBoxDoc0(boxID)
// after
boxID := "20240601120000-newbox01"
ensureBoxDoc0(boxID)
Defensive patterns

Strategy: type-guard

Type guard

// Go predicate mirroring ast.IsNodeIDPattern
var nodeIDRe = regexp.MustCompile(`^\d{14}-[a-z0-9]{7}$`)

func isValidNodeID(id string) bool {
	return nodeIDRe.MatchString(id)
}

// use before the call
if !isValidNodeID(boxID) {
	return fmt.Errorf("refusing to ensure box doc: bad notebook ID %q", boxID)
}
_, _ = model.EnsureBoxDoc(boxID)

Try / catch

if _, err := model.EnsureBoxDoc(boxID); err != nil {
	if strings.Contains(err.Error(), "invalid box ID") {
		// input problem: fix the caller, do not retry
	}
	return err
}

Prevention

When it happens

Trigger: A notebook folder under data/ whose name is not a 20-char node ID (hand-created folder) reaches mount; a corrupted conf.json notebooks list; a repo snapshot referencing a box with a garbage ID during sync apply; calling notebook APIs with arbitrary strings instead of IDs from /api/notebook/lsNotebooks.

Common situations: Users creating notebook folders manually in the file manager; workspace files touched by external tools; damaged sync snapshots; scripts calling /api/notebook/* with made-up IDs.

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@afa823b6b4 (2026-08-18). Data as JSON: /api/errors/a19632856e32a71c. Report an issue: GitHub.