{"record":{"id":"ea1696ccf17c89a8","repo":"siyuan-note/siyuan","slug":"decode-session-data-failed-w","errorCode":null,"errorMessage":"decode session data failed: %w","messagePattern":"decode session data failed: %w","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/agent/session.go","lineNumber":334,"sourceCode":"\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\")\n\tdelete(newData, \"lastCommittedTurnID\")\n\tcommitTurnID := meta.CommitTurnID\n\tif commitTurnID == \"\" {\n\t\tcommitTurnID = meta.RecoveryTurnID\n\t}\n\n\tcurrentRevision := int64(0)\n\tcurrentCommittedTurnID := \"\"\n\texisting, err := os.ReadFile(path)\n\tif err == nil && len(existing) > 0 {\n\t\tvar existingData map[string]any","sourceCodeStart":316,"sourceCodeEnd":352,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/9f775e8a12daef8255556097396f9b2739078892/kernel/agent/session.go#L316-L352","documentation":"After metadata validation, SaveSessionState decodes the full payload into a generic map. If that second gulu.JSON.UnmarshalJSON fails, it wraps the underlying error as \"decode session data failed: %w\". This catches payloads that parse for sessionMeta but are not valid JSON overall, or contain structurally incompatible values for the full decode pass.","triggerScenarios":"Sending truncated or syntactically invalid JSON (metadata parse succeeded earlier only if the error appeared later in the body — otherwise see error 107); duplicating or corrupting bytes during transport; a custom encoder emitting invalid JSON (NaN, trailing commas).","commonSituations":"Interrupted uploads leaving partial bodies; proxy/encoding layers mangling the payload; client bug writing NaN/Infinity into numeric fields.","solutions":["Inspect the wrapped inner error (%w) to find the exact JSON syntax offset","Re-send the full, complete session JSON from the client","Fix the client serializer to emit strict JSON (no NaN, no trailing commas)","Validate the payload with a strict JSON parser client-side before sending"],"exampleFix":"// before\npayload := fmt.Sprintf(\"{\"id\":%q,,\"entries\":%v}\", id, entries) // malformed\nSaveSessionState([]byte(payload))\n// after\npayload, err := json.Marshal(sessionObj)\nif err != nil { return err }\nSaveSessionState(payload)","handlingStrategy":"try-catch","validationCode":"var probe any\nif err := json.Unmarshal(payload, &probe); err != nil { /* fix payload before sending */ }","typeGuard":null,"tryCatchPattern":"if _, _, err := SaveSessionState(data); err != nil && strings.HasPrefix(err.Error(), \"decode session data failed\") {\n    var perr *json.SyntaxError\n    if errors.As(err, &perr) { log.Printf(\"bad JSON at offset %d\", perr.Offset) }\n}","preventionTips":["Marshal with a strict JSON encoder; never concatenate JSON fragments","Ensure uploads complete fully before save (no truncated bodies)","Test payloads containing special floats/unicode for encoder strictness"],"tags":["go","agent","json","decode"],"backgroundTag":"json-unmarshal-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"}