siyuan-note/siyuan · error

unmarshal box document metadata failed: %w

Error message

unmarshal box document metadata failed: %w

What it means

readBoxDocID parses <workspace>/data/<notebookID>/.siyuan/boxDoc.json, which binds the notebook to its box document (spec + boxDocID, kernel/model/box_doc.go:39-47). If the file exists but gulu.JSON cannot unmarshal it, the parse error is wrapped with this message. A missing file is fine (returns nil error); only malformed content fails.

Source

Thrown at kernel/model/box_doc.go:71

func boxDocPath(boxID string) string {
	if "" == boxID {
		return ""
	}
	return "/" + boxID + ".sy"
}

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)

View on GitHub (pinned to afa823b6b4)

Solutions

  1. Restore data/<boxID>/.siyuan/boxDoc.json from backup/history
  2. If no backup exists, delete the corrupted boxDoc.json — readBoxDocID tolerates absence and the kernel rewrites it (spec 1, boxDocID = box ID) on next notebook write
  3. Exclude the workspace from sync-agent partial writes, or pause sync during kernel shutdown
Defensive patterns

Strategy: try-catch

Try / catch

try { readBoxDocID(boxID) } catch (e) { if (e.Contains("unmarshal box document metadata")) { restoreOrDeleteBoxDocJson(boxID); /* kernel regenerates it */ } }

Prevention

When it happens

Trigger: Any notebook open/mount path that calls readBoxDocID when boxDoc.json is truncated or corrupt: partial write after a crash, external editor saving invalid JSON, disk or sync corruption.

Common situations: Power loss mid-write; sync clients (Dropbox/OneDrive) producing conflict-truncated files; hand-editing the .siyuan metadata directory.

Understand the failure class

Background: JSON parse error: "Unexpected token" / "not valid JSON" / "failed to parse" — what JSON parsers are really complaining about — this error's family across 45 libraries.

Related errors


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