{"record":{"id":"92ecde7b8f77fbf3","repo":"siyuan-note/siyuan","slug":"invalid-session-data","errorCode":null,"errorMessage":"invalid session data","messagePattern":"invalid session data","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/agent/session.go","lineNumber":323,"sourceCode":"\t\t}\n\t}\n\tpermissionMode, err := resolveSessionPermissionModeLocked(id, session)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tsession[\"permissionMode\"] = permissionMode\n\treturn session, nil\n}\n\nfunc SaveSession(data []byte) (int64, error) {\n\trevision, _, err := SaveSessionState(data)\n\treturn revision, err\n}\n\nfunc SaveSessionState(data []byte) (int64, map[string]any, error) {\n\tvar meta sessionMeta\n\tif err := gulu.JSON.UnmarshalJSON(data, &meta); err != nil || meta.ID == \"\" || !isValidSessionID(meta.ID) {\n\t\treturn 0, nil, fmt.Errorf(\"invalid session data\")\n\t}\n\tlock := sessionLock(meta.ID)\n\tlock.Lock()\n\tdefer lock.Unlock()\n\n\tdir := filepath.Join(sessionsDir(), meta.ID)\n\tpath := filepath.Join(dir, \"session.json\")\n\n\tvar newData map[string]any\n\tif err := gulu.JSON.UnmarshalJSON(data, &newData); err != nil {\n\t\treturn 0, nil, fmt.Errorf(\"decode session data failed: %w\", err)\n\t}\n\tdelete(newData, \"expectedRevision\")\n\tdelete(newData, \"commitTurnID\")\n\tdelete(newData, \"recoveryTurnID\")\n\tdelete(newData, \"recoveryState\")\n\tdelete(newData, \"recoveryRevision\")\n\tdelete(newData, \"agentRunning\")","sourceCodeStart":305,"sourceCodeEnd":341,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/9f775e8a12daef8255556097396f9b2739078892/kernel/agent/session.go#L305-L341","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nbody := map[string]any{\"entries\": entries} // id missing\nSaveSessionState(marshal(body)) // invalid session data\n// after\nbody := map[string]any{\"id\": sessionID, \"entries\": entries}\nif sessionID == \"\" || !isValidSessionID(sessionID) {\n    return errors.New(\"refusing to save: bad id\")\n}\nSaveSessionState(marshal(body))","handlingStrategy":"validation","validationCode":"var meta struct{ ID string `json:\"id\"` }\nif json.Unmarshal(payload, &meta) != nil || meta.ID == \"\" || !isValidSessionID(meta.ID) { return errors.New(\"payload missing valid id\") }","typeGuard":"func payloadHasValidID(payload []byte) bool {\n    var m struct{ ID string `json:\"id\"` }\n    return json.Unmarshal(payload, &m) == nil && m.ID != \"\" && isValidSessionID(m.ID)\n}","tryCatchPattern":"if _, _, err := SaveSessionState(data); err != nil && strings.Contains(err.Error(), \"invalid session data\") {\n    // fix payload: ensure valid JSON and a well-formed id, then resend\n}","preventionTips":["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"],"tags":["go","agent","validation","json","schema"],"backgroundTag":"schema-validation-failed","analyzedSha":"9f775e8a12daef8255556097396f9b2739078892","analyzedAt":"2026-09-19T03:17:15.984Z","contentChangedAt":"2026-09-19T03:17:15.984Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}