siyuan-note/siyuan · error
encode session data failed: %w
Error message
encode session data failed: %w
What it means
The final serialization step of SaveSessionState: the merged session map (client payload + preserved unknown fields from disk + new revision) is written via gulu.JSON.MarshalIndentJSON (kernel/agent/session.go:409-411). If any value in that map cannot be represented in JSON, the save fails with 'encode session data failed' before anything is written — the on-disk file stays untouched.
Source
Thrown at kernel/agent/session.go:411
if !isRuntimeTurnTerminal(runtime.ActiveTurn) {
return currentRevision, nil, ErrRuntimeNotFinalized
}
if err := applyRuntimeTurnToSessionLocked(newData, runtime.ActiveTurn); err != nil {
return currentRevision, nil, err
}
} else if currentCommittedTurnID != commitTurnID {
return currentRevision, nil, ErrSessionConflict
}
}
newRevision := currentRevision + 1
newData["revision"] = newRevision
if commitTurnID != "" {
newData["lastCommittedTurnID"] = commitTurnID
}
data, err = gulu.JSON.MarshalIndentJSON(newData, "", "\t")
if err != nil {
return currentRevision, nil, fmt.Errorf("encode session data failed: %w", err)
}
if err := os.MkdirAll(dir, 0755); err != nil {
return currentRevision, nil, fmt.Errorf("create session dir failed: %w", err)
}
if err := filelock.WriteFile(path, data); err != nil {
return currentRevision, nil, fmt.Errorf("save session file failed: %w", err)
}
if commitTurnID != "" {
if err := markRuntimeCommittedLocked(meta.ID, commitTurnID); err != nil {
logging.LogWarnf("commit agent runtime failed: %s", err)
}
}
title, _ := newData["title"].(string)
if title == "" {
title = "AI Agent"
}View on GitHub (pinned to afa823b6b4)
Solutions
- Sanitize the payload to pure JSON types (string, number within ±1.7e308, bool, nil, slices, maps) before calling SaveSession from Go
- Replace NaN/Inf with null or clamped values client-side
- Log the failing map and bisect which key holds the unrepresentable value
- No disk state was modified — after fixing the value, the same save can be retried safely
Defensive patterns
Strategy: try-catch
Validate before calling
// For Go callers: ensure every value in the map is JSON-representable
func jsonSafe(v any) bool { _, err := json.Marshal(v); return err == nil } Type guard
null
Try / catch
catch (e) {
if (/encode session data failed/.test(e?.data?.msg ?? '')) {
// sanitize payload to plain JSON types and retry; disk state is untouched
}
} Prevention
- Build session payloads via JSON round-trips, not by injecting live Go values
- Replace NaN/Inf with null before saving
When it happens
Trigger: SaveSession called with a payload containing non-marshalable values — NaN/Inf numbers, or Go chan/func/complex values from in-process callers. Pure-JSON HTTP payloads decoded into map[string]any practically cannot trigger this; it guards programmatic callers and future field types.
Common situations: Kernel/plugin code building the session map in Go instead of round-tripping JSON; float NaN produced by arithmetic on client-side and smuggled in as a string then re-typed; essentially a canary for invariant breakage rather than a user-facing condition.
Related errors
- decode session data failed: %w
- agent session revision conflict
- invalid session id
- invalid session data
- decode existing session data failed: %w
AI-assisted analysis of siyuan-note/siyuan@afa823b6b4 (2026-08-18).
Data as JSON: /api/errors/9b858ec5f3489dda.
Report an issue: GitHub.