siyuan-note/siyuan · error

invalid box document ID [%s]

Error message

invalid box document ID [%s]

What it means

Thrown by readBoxDocID in kernel/model/box_doc.go when parsing data/<boxID>/.siyuan/boxDoc.json, the metadata file that registers a notebook's hidden 'box document'. The boxDocID field fails ast.IsNodeIDPattern, i.e. it is not a valid SiYuan node ID (14-digit timestamp, dash, 7-char suffix, e.g. 20240102150405-a1b2c3d). The file is treated as untrusted on-disk state, so any malformed value aborts box-doc initialization for that notebook.

Source

Thrown at kernel/model/box_doc.go:77

func readBoxDocID(boxID string) (ret string, err error) {
	data, err := filelock.ReadFile(boxDocMetaPath(boxID))
	if err != nil {
		if errors.Is(err, os.ErrNotExist) {
			err = nil
		}
		return
	}

	meta := &boxDocMeta{}
	if err = gulu.JSON.UnmarshalJSON(data, meta); err != nil {
		return "", fmt.Errorf("unmarshal box document metadata failed: %w", err)
	}
	if boxDocMetaSpec != meta.Spec {
		return "", fmt.Errorf("unsupported box document metadata spec [%d]", meta.Spec)
	}
	if !ast.IsNodeIDPattern(meta.BoxDocID) {
		return "", fmt.Errorf("invalid box document ID [%s]", meta.BoxDocID)
	}
	if boxID != meta.BoxDocID {
		return "", fmt.Errorf("box document ID [%s] does not match box ID [%s]", meta.BoxDocID, boxID)
	}
	return boxID, nil
}

func writeBoxDocID(boxID string) error {
	meta := &boxDocMeta{Spec: boxDocMetaSpec, BoxDocID: boxID}
	data, err := gulu.JSON.MarshalIndentJSON(meta, "", "  ")
	if err != nil {
		return fmt.Errorf("marshal box document metadata failed: %w", err)
	}
	return filelock.WriteFile(boxDocMetaPath(boxID), data)
}

func IsBoxDocEnabled() bool {
	return nil != Conf && nil != Conf.FileTree && nil != Conf.FileTree.BoxDocEnabled && *Conf.FileTree.BoxDocEnabled

View on GitHub (pinned to afa823b6b4)

Solutions

  1. Open <workspace>/data/<boxID>/.siyuan/boxDoc.json and inspect the boxDocID value; the filename <boxID> is the expected value
  2. Set the file to {"spec":1,"boxDocID":"<boxID>"} where <boxID> is the notebook folder name, provided it matches the node-ID pattern
  3. If the meta is unrecoverable, delete boxDoc.json (the kernel regenerates it on the next EnsureBoxDoc, reusing the existing hidden doc) and re-run the mount/open operation
  4. Check kernel logs for 'initialized box document' to confirm recovery

Example fix

// before: data/20230102150405-a1b2c3d/.siyuan/boxDoc.json
{"spec":1,"boxDocID":""}
// after
{"spec":1,"boxDocID":"20230102150405-a1b2c3d"}
Defensive patterns

Strategy: try-catch

Try / catch

boxDocID, err := model.EnsureBoxDoc(boxID)
if err != nil {
	if strings.Contains(err.Error(), "invalid box document ID") {
		log.Printf("boxDoc.json for %s is malformed; regenerate it", boxID)
	}
	return err
}

Prevention

When it happens

Trigger: Calling EnsureBoxDoc (notebook mount at kernel/model/mount.go:475, sync apply at kernel/model/repository.go:2509, or feature refresh) for a notebook whose boxDoc.json was hand-edited, truncated by a partial write, corrupted by an interrupted sync, or written by a different spec version. Empty or garbage boxDocID values reproduce it directly.

Common situations: Workspace restored from an incomplete backup; sync-conflict artifacts inside the notebook's .siyuan folder; external tools that rewrite/pretty-print the JSON and blank the field; manually copying a notebook folder and hand-editing only conf.json.

Related errors


AI-assisted analysis of siyuan-note/siyuan@afa823b6b4 (2026-08-18). Data as JSON: /api/errors/158ad0520630287f. Report an issue: GitHub.