siyuan-note/siyuan · error
map[string]apicontract.JSONValue
Error message
map[string]apicontract.JSONValue
What it means
SystemSetUILayout (/api/system/setUILayout) stores the layout as map[string]JSONValue; this rewritten error surfaces when that map cannot be produced from the `layout` field. The message replaces the internal type name map[string]apicontract.JSONValue with conf.UILayout so the user sees the real config type. It means the `layout` value (or one of its entries) has a shape that does not match the expected UI-layout structure.
Solutions
- Send layout as a JSON object matching the conf.UILayout shape (typically {"<paneID>":{...layout...}})
- Export the current layout via the UI and reuse that structure as a template
- If unsure, omit `layout` or send null to reset rather than sending a guessed object
Example fix
// before
{"layout": "[{\"id\":\"root\"}]"}
// after
{"layout": {"root": {"direction": 1, "instance": "..."}}} Defensive patterns
Strategy: type-guard
Validate before calling
const validLayout = l => l === undefined || l === null || (typeof l === 'object' && !Array.isArray(l));
Type guard
const isUILayout = (v) => typeof v === 'object' && (v === null || !Array.isArray(v));
Try / catch
const res = await fetchPost('/api/system/setUILayout', {layout}); if (res.code !== 0) { /* res.data.layoutError or res.msg describes the field mismatch */ } Prevention
- Round-trip layout through the UI export to get a valid shape
- Never send a stringified layout object
- Send null to reset instead of a guessed structure
When it happens
Trigger: POSTing /api/system/setUILayout with layout as a non-object (string/array), or with nested entries whose types conflict during legacy JSON value decoding; an absent layout field decodes as null and is tolerated, so the error comes from an actually present but malformed value.
Common situations: A plugin or script persisting a custom layout object that predates the current schema; hand-edited layout JSON with wrong nesting; restoring a layout blob from an older SiYuan version.
Related errors
- invalid config.
- struct field Sync .
- block ID must be text
- block operation result must be text, block IDs or null
- builtin color must not be null
AI-assisted analysis of siyuan-note/siyuan@9f775e8a12 (2026-09-19).
Data as JSON: /api/errors/505d54a98342956c.
Report an issue: GitHub.
Appendix: source
Thrown at kernel/apicontract/system_input.go:31
config, err := decodeSettingConfig(reader, SystemGetChangelog.Definition().Path, false)
if err != nil {
return r, nil
}
r.Force, err = legacyField[bool](config.fields, "force", "Boolean", false)
return
}
SystemSetUILayout.decodeRequest = func(reader io.Reader) (r SystemUILayoutRequest, err error) {
config, err := decodeSettingConfig(reader, SystemSetUILayout.Definition().Path, false)
if err != nil {
return r, err
}
raw := config.fields["layout"]
if len(raw) == 0 {
raw = []byte("null")
}
r.Layout, r.layoutError = legacyJSONValue[map[string]JSONValue](raw)
if r.layoutError != nil {
r.layoutError = errors.New(strings.ReplaceAll(r.layoutError.Error(), "map[string]apicontract.JSONValue", "conf.UILayout"))
}
return
}
SystemLoginAuth.decodeRequest = func(reader io.Reader) (r SystemLoginAuthRequest, err error) {
config, err := decodeSettingConfig(reader, SystemLoginAuth.Definition().Path, false)
if err != nil {
return r, err
}
r.AuthCode, r.authCodeError = legacyField[string](config.fields, "authCode", "String", true)
r.Captcha, r.captchaError = legacyField[string](config.fields, "captcha", "String", false)
r.RememberMe, _ = legacyField[bool](config.fields, "rememberMe", "Boolean", false)
return
}
SystemOIDCStart.decodeRequest = func(reader io.Reader) (r SystemOIDCStartRequest, err error) {
r.parseError = json.NewDecoder(reader).Decode(&r)
return
}
SystemOIDCMobileCallback.decodeRequest = func(reader io.Reader) (r SystemOIDCMobileRequest, err error) {View on GitHub (pinned to 9f775e8a12)