siyuan-note/siyuan · error
marshal box conf [%s] failed: %w
Error message
marshal box conf [%s] failed: %w
What it means
In Box.SaveConf, after prepareBoxConfForSave succeeds, gulu.JSON.MarshalIndentJSON(persisted, '', ' ') is called; on failure it returns fmt.Errorf('marshal box conf [%s] failed: %w', confPath, err). conf.BoxConf is a plain serializable struct, so under normal operation this never fires — it would only fire if the struct gained a non-marshalable field (chan/func/pointer cycle) or encoding encountered a truly malformed value.
Source
Thrown at kernel/model/box.go:291
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 251596fc0d)
Solutions
- Inspect the BoxConf struct definition (kernel/conf) for any field that JSON cannot encode (func, chan, unsafe.Pointer, or a cycle).
- Add json:"-" tags or remove the offending field; ensure all fields are plain data.
- Write a unit test that round-trips conf.NewBoxConf() through json.Marshal to catch regressions.
Example fix
// before: a func field breaks marshalling
type BoxConf struct {
Encrypted bool
onChange func()
}
// after: exclude non-serializable fields
type BoxConf struct {
Encrypted bool
onChange func() `json:"-"`
} Defensive patterns
Strategy: validation
Validate before calling
// Catch marshal regressions in tests before they hit production.
func TestBoxConfSerializable(t *testing.T) {
b, err := json.Marshal(conf.NewBoxConf())
if err != nil || !json.Valid(b) { t.Fatalf("BoxConf not serializable: %v", err) }
} Prevention
- Do not add func/chan fields to BoxConf without a json:"-" tag.
- Add a JSON round-trip test for any struct persisted to conf.json.
When it happens
Trigger: Effectively only reachable from a bug in the BoxConf type definition (a developer adding an unserializable field) or a deeply corrupted in-memory BoxConf. Not a runtime/environmental failure.
Common situations: A code change introduced an unexported chan/func field on BoxConf or a circular reference between conf structs; an experimental branch mutated the struct in a non-serializable way.
Related errors
- prepare box conf [%s] failed: %w
- mkdir box conf dir failed: %w
- write box conf [%s] failed: %w
- encrypted notebook key material is missing
- encrypted notebook is locked, please unlock it first
AI-assisted analysis of siyuan-note/siyuan@251596fc0d (2026-08-12).
Data as JSON: /api/errors/aff246df23331d05.
Report an issue: GitHub.