siyuan-note/siyuan · error
invalid session data
Error message
invalid session data
What it means
SaveSessionState first decodes the payload's metadata (sessionMeta). If the JSON cannot be unmarshalled, or meta.ID is empty or fails isValidSessionID, the whole payload is deemed unusable and it returns "invalid session data" without creating locks, directories, or files.
Solutions
- Ensure the payload is valid JSON and includes a non-empty, well-formed id field
- Validate the id client-side with the same charset rules before sending
- Log/inspect the raw request body when this fires — it usually means the sender serialized the wrong object
- Update the client after any schema change to sessionMeta so field names still match
Example fix
// before
body := map[string]any{"entries": entries} // id missing
SaveSessionState(marshal(body)) // invalid session data
// after
body := map[string]any{"id": sessionID, "entries": entries}
if sessionID == "" || !isValidSessionID(sessionID) {
return errors.New("refusing to save: bad id")
}
SaveSessionState(marshal(body)) Defensive patterns
Strategy: validation
Validate before calling
var meta struct{ ID string `json:"id"` }
if json.Unmarshal(payload, &meta) != nil || meta.ID == "" || !isValidSessionID(meta.ID) { return errors.New("payload missing valid id") } Type guard
func payloadHasValidID(payload []byte) bool {
var m struct{ ID string `json:"id"` }
return json.Unmarshal(payload, &m) == nil && m.ID != "" && isValidSessionID(m.ID)
} Try / catch
if _, _, err := SaveSessionState(data); err != nil && strings.Contains(err.Error(), "invalid session data") {
// fix payload: ensure valid JSON and a well-formed id, then resend
} Prevention
- Always serialize the full session object including id
- Use the language's JSON marshaler, never hand-built strings
- Add a client-side schema check before save requests
When it happens
Trigger: Posting session data that is not valid JSON; JSON lacking the required id field; an id present but malformed (path separators, '..', illegal characters); wrong Content-Type leading to a non-JSON body reaching the parser.
Common situations: Client sends a partial/updated session object that dropped the id field; encoding layer sends form data instead of JSON; hand-crafted API calls with a typo'd or fabricated id.
Understand the failure class
Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.
Related errors
- decode existing session data failed
- decode session data failed
- frontend capability description is required
- invalid frontend capability
- invalid pinned document
AI-assisted analysis of siyuan-note/siyuan@9f775e8a12 (2026-09-19).
Data as JSON: /api/errors/92ecde7b8f77fbf3.
Report an issue: GitHub.
Appendix: source
Thrown at kernel/agent/session.go:323
}
}
permissionMode, err := resolveSessionPermissionModeLocked(id, session)
if err != nil {
return nil, err
}
session["permissionMode"] = permissionMode
return session, nil
}
func SaveSession(data []byte) (int64, error) {
revision, _, err := SaveSessionState(data)
return revision, err
}
func SaveSessionState(data []byte) (int64, map[string]any, error) {
var meta sessionMeta
if err := gulu.JSON.UnmarshalJSON(data, &meta); err != nil || meta.ID == "" || !isValidSessionID(meta.ID) {
return 0, nil, fmt.Errorf("invalid session data")
}
lock := sessionLock(meta.ID)
lock.Lock()
defer lock.Unlock()
dir := filepath.Join(sessionsDir(), meta.ID)
path := filepath.Join(dir, "session.json")
var newData map[string]any
if err := gulu.JSON.UnmarshalJSON(data, &newData); err != nil {
return 0, nil, fmt.Errorf("decode session data failed: %w", err)
}
delete(newData, "expectedRevision")
delete(newData, "commitTurnID")
delete(newData, "recoveryTurnID")
delete(newData, "recoveryState")
delete(newData, "recoveryRevision")
delete(newData, "agentRunning")View on GitHub (pinned to 9f775e8a12)