siyuan-note/siyuan · error

marshal box document metadata failed: %w

Error message

marshal box document metadata failed: %w

What it means

This error is returned by writeBoxDocID when gulu.JSON.MarshalIndentJSON fails to serialize the boxDocMeta struct into indented JSON. Since the struct contains only an int and a string, marshaling practically never fails — it would require a unsupported value reaching the encoder — so this error is an internal-invariant safeguard; the error is wrapped with %w and propagates from ensureBoxDoc0 and the import path.

Source

Thrown at kernel/model/box_doc.go:89

		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
}

func hiddenBoxDocRootIDs() (ret []string) {
	if IsBoxDocEnabled() || nil == Conf {
		return
	}
	for _, box := range Conf.GetOpenedBoxes() {
		ret = append(ret, box.ID)
	}
	return
}

View on GitHub (pinned to 8641553a1f)

Solutions

  1. Inspect the wrapped error's message from the log to identify which value the encoder rejected
  2. Review recent changes to the boxDocMeta struct or gulu JSON code; revert any field that is not JSON-encodable
  3. Update to a current SiYuan build in case a dependency defect in gulu was fixed
  4. If seen in production with stock code, capture the full stack trace and report it upstream, since stock boxDocMeta should always marshal

Example fix

// before: non-encodable field breaks marshal
type boxDocMeta struct {
    Spec     int    `json:"spec"`
    BoxDocID string `json:"boxDocID"`
    Hooks    func() `json:"hooks"` // unsupported by encoder
}
// after: keep only JSON-encodable fields
type boxDocMeta struct {
    Spec     int    `json:"spec"`
    BoxDocID string `json:"boxDocID"`
}
Defensive patterns

Strategy: try-catch

Validate before calling

null

Type guard

null

Try / catch

if err := writeBoxDocID(boxID); err != nil {
    if strings.Contains(err.Error(), "marshal box document metadata failed") {
        // serialization bug: log full wrapped error and skip sync of this change
        logging.LogErrorf("box doc metadata marshal failed for box %s: %s", boxID, err)
        return err
    }
    return err
}

Prevention

When it happens

Trigger: writeBoxDocID(boxID) is called by ensureBoxDoc0 (when the stored box document ID differs from the box ID, e.g. after enabling the box-doc feature or creating the box document) or during importSY0 / TestImportNotebookSYAsChildDocuments, and the JSON encoder fails on the meta struct.

Common situations: Essentially only reproducible by modifying the boxDocMeta struct to include a field the JSON encoder cannot handle (e.g. a chan, func, or cyclic value), or by an encoding defect in a custom gulu build; users should not encounter it in normal operation.

Understand the failure class

Background: json.Marshal / "failed to marshal" errors in Go: why "unsupported type" happens and how to fix it — this error's family across 22 libraries.

Related errors


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