siyuan-note/siyuan · error

marshal box conf [ ] failed

Error message

marshal box conf [%s] failed: %w

What it means

SaveConf persists a notebook (box) configuration to <DataDir>/<boxID>/.siyuan/conf.json. Before writing it sanitizes the conf via prepareBoxConfForSave, then serializes it with gulu.JSON.MarshalIndentJSON. This error is returned when JSON marshaling of the prepared configuration fails, which is nearly impossible for plain BoxConf structs but can occur if the structure holds unmarshalable values (e.g. invalid types injected through channels, maps, or func fields).

Solutions

  1. Inspect the BoxConf object passed to SaveConf for fields holding func, channel, or invalid values and reset them to defaults
  2. Recreate the BoxConf with conf.NewBoxConf() and copy only known-good scalar fields before calling SaveConf
  3. Log the wrapped inner error (%w) to identify which value failed to marshal
  4. Update siyuan-note/gulu to the latest version in case of an encoding bug

Example fix

// before
conf.FuncField = myFunc // unmarshalable
err := box.SaveConf(conf)
// after
conf.FuncField = nil // or remove unsupported field
err := box.SaveConf(conf)
Defensive patterns

Strategy: validation

Validate before calling

// Go: ensure conf contains only marshalable fields before SaveConf
b, err := json.Marshal(conf)
if err != nil {
    return fmt.Errorf("box conf not serializable: %w", err)
}

Try / catch

if err := box.SaveConf(conf); err != nil {
    log.Printf("save conf failed: %v", err) // wrapped marshal error
}

Prevention

When it happens

Trigger: Calling Box.SaveConf(conf) when gulu.JSON.MarshalIndentJSON(persisted) fails; in practice this happens only if the persisted BoxConf contains a value encoding/json cannot marshal (func, channel, or a custom Marshaler returning an error), e.g. after programmatic corruption of the conf object.

Common situations: Plugin or test code mutating a BoxConf struct with unsupported field values; memory pressure bugs; a defective custom marshaling hook; essentially never from normal UI use.

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@9f775e8a12 (2026-09-19). Data as JSON: /api/errors/aff246df23331d05. Report an issue: GitHub.

Appendix: source

Thrown at kernel/model/box.go:342

	if ret.Encrypted {
		if err = revealBoxMetadataIfUnlocked(box.ID, ret); err != nil {
			logging.LogErrorf("decrypt encrypted notebook metadata [%s] failed: %s", box.ID, err)
		}
	} else {
		ret.Icon = filterBoxIcon(ret.Icon)
	}
	return
}

func (box *Box) SaveConf(conf *conf.BoxConf) error {
	confPath := filepath.Join(util.DataDir, box.ID, ".siyuan/conf.json")
	persisted, err := prepareBoxConfForSave(box.ID, conf)
	if err != nil {
		return fmt.Errorf("prepare box conf [%s] failed: %w", confPath, err)
	}
	newData, err := gulu.JSON.MarshalIndentJSON(persisted, "", "  ")
	if err != nil {
		return fmt.Errorf("marshal box conf [%s] failed: %w", confPath, err)
	}

	oldData, err := filelock.ReadFile(confPath)
	if err != nil {
		if err = box.saveConf0(newData); err != nil {
			return err
		}
		return syncBoxConfCryptoBackup(box.ID, persisted)
	}

	if bytes.Equal(newData, oldData) {
		return syncBoxConfCryptoBackup(box.ID, persisted)
	}

	if err = box.saveConf0(newData); err != nil {
		return err
	}
	return syncBoxConfCryptoBackup(box.ID, persisted)

View on GitHub (pinned to 9f775e8a12)