siyuan-note/siyuan · error

unsupported box document metadata spec [%d]

Error message

unsupported box document metadata spec [%d]

What it means

This error is returned by readBoxDocID when boxDoc.json parses but its `spec` field does not equal the version this kernel build supports (boxDocMetaSpec = 1). The spec field guards the box-document metadata format: if a future or past SiYuan version wrote a different schema, this build refuses to interpret it instead of misreading unknown fields. It is a forward/backward compatibility check on the metadata format version.

Source

Thrown at kernel/model/box_doc.go:74

	}
	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)
	}
	return filelock.WriteFile(boxDocMetaPath(boxID), data)
}

View on GitHub (pinned to 8641553a1f)

Solutions

  1. Use the SiYuan version that matches the spec written in boxDoc.json (upgrade the kernel if the file is newer)
  2. Check the file's spec value; if the file was hand-edited or the spec type changed, set it to the numeric literal 1
  3. If you intentionally want the feature re-initialized, back up the notebook, delete .siyuan/boxDoc.json, and let ensureBoxDoc0 recreate it with the current spec
  4. Do not edit spec by hand to silence the error unless you understand the schema — a mismatched schema will surface as other errors

Example fix

// before: missing/string spec field
{"boxDocID": "20240101120000-abcdefg"}
{"spec": "1", "boxDocID": "20240101120000-abcdefg"}
// after: numeric spec matching boxDocMetaSpec (= 1)
{"spec": 1, "boxDocID": "20240101120000-abcdefg"}
Defensive patterns

Strategy: validation

Validate before calling

// Go: pre-check the spec field before invoking kernel APIs that touch box metadata
raw, _ := os.ReadFile(filepath.Join(util.DataDir, boxID, ".siyuan", "boxDoc.json"))
var m struct {
    Spec int `json:"spec"`
}
if json.Unmarshal(raw, &m) == nil && m.Spec != 1 {
    // unsupported spec: upgrade/downgrade SiYuan or remove the file to regenerate
    fmt.Printf("boxDoc.json spec=%d, this build supports spec=1\n", m.Spec)
}

Type guard

func specSupported(raw []byte) bool {
    var m struct {
        Spec *int `json:"spec"`
    }
    if json.Unmarshal(raw, &m) != nil || m.Spec == nil {
        return false
    }
    return *m.Spec == 1
}

Try / catch

if _, err := EnsureBoxDoc(boxID); err != nil {
    if strings.Contains(err.Error(), "unsupported box document metadata spec") {
        // migrate: back up, remove stale metadata, let the kernel recreate it
        os.Rename(filepath.Join(util.DataDir, boxID, ".siyuan", "boxDoc.json"),
            filepath.Join(util.DataDir, boxID, ".siyuan", "boxDoc.json.bak"))
        _, err = EnsureBoxDoc(boxID)
    }
}

Prevention

When it happens

Trigger: readBoxDocID(boxID) reads a boxDoc.json whose numeric `spec` value is anything other than 1 — e.g. the file was produced by a newer SiYuan build that bumped the spec, or was hand-written with spec 0, "1" (string instead of number decodes as 0), or a missing spec field (decodes as 0).

Common situations: Rolling back the kernel to an older version after a newer version upgraded the metadata spec; mixing workspace data between different SiYuan versions via sync; hand-crafting the metadata file without knowing the required spec value; a JSON schema edit that changed spec's type so it unmarshals as 0.

Related errors


AI-assisted analysis of siyuan-note/siyuan@8641553a1f (2026-09-11). Data as JSON: /api/errors/891ecc532615952b. Report an issue: GitHub.