siyuan-note/siyuan · error

decode existing session data failed: %w

Error message

decode existing session data failed: %w

What it means

When saving, SaveSessionState reads the existing data/storage/ai/agent/sessions/<id>/session.json to compute the current revision (kernel/agent/session.go:349-353). If the file exists and is non-empty but is not valid JSON, the save aborts with 'decode existing session data failed' — the on-disk session is corrupt, so the kernel refuses to guess a revision or overwrite authoritative content blindly.

Source

Thrown at kernel/agent/session.go:353

	delete(newData, "expectedRevision")
	delete(newData, "commitTurnID")
	delete(newData, "recoveryTurnID")
	delete(newData, "recoveryState")
	delete(newData, "recoveryRevision")
	delete(newData, "agentRunning")
	delete(newData, "lastCommittedTurnID")
	commitTurnID := meta.CommitTurnID
	if commitTurnID == "" {
		commitTurnID = meta.RecoveryTurnID
	}

	currentRevision := int64(0)
	currentCommittedTurnID := ""
	existing, err := os.ReadFile(path)
	if err == nil && len(existing) > 0 {
		var existingData map[string]any
		if err := gulu.JSON.UnmarshalJSON(existing, &existingData); err != nil {
			return 0, nil, fmt.Errorf("decode existing session data failed: %w", err)
		} else {
			currentRevision = numberToInt64(existingData["revision"])
			currentCommittedTurnID, _ = existingData["lastCommittedTurnID"].(string)
			if commitTurnID != "" && currentCommittedTurnID == commitTurnID {
				// 提交响应丢失后,客户端可能原样重试同一个 commitTurnID。此判断要先于修订号校验,
				// 并且不能再用客户端快照覆盖已经由 runtime 生成的权威内容。
				if err := markRuntimeCommittedLocked(meta.ID, commitTurnID); err != nil {
					logging.LogWarnf("clean committed agent runtime failed: %s", err)
				}
				return currentRevision, existingData, nil
			}
			if meta.ExpectedRevision != nil && *meta.ExpectedRevision != currentRevision {
				return currentRevision, nil, ErrSessionConflict
			}
			for k, v := range existingData {
				if _, ok := newData[k]; !ok {
					// messages 是已废弃的旧会话字段,不再带入新格式;其他未知字段原样保留,
					// 避免前后端版本不一致时擦除较新版本写入的数据。

View on GitHub (pinned to afa823b6b4)

Solutions

  1. Inspect workspace/data/storage/ai/agent/sessions/<id>/session.json — confirm where the JSON breaks (truncation vs. merge conflict markers)
  2. Restore the file from your backup or the sync tool's conflict copy
  3. If the session is disposable, DELETE /api/ai/agent/removeSession and let the client create a fresh session (history in that session is lost)
  4. If it recurs, stop putting the workspace on a cloud-synced folder and check disk health
Defensive patterns

Strategy: fallback

Validate before calling

// Health check: verify the on-disk session still parses before a critical save
const s = await fetchPost('/api/ai/agent/getSession', {id}); // fails fast if session.json is corrupt
if (s.code === -1 && /decode existing/i.test(s.msg)) { /* quarantine session, alert user */ }

Type guard

null

Try / catch

catch (e) {
  if (/decode existing session data/.test(e?.data?.msg ?? '')) {
    // stop saving; offer restore-from-backup or removeSession to reset
  }
}

Prevention

When it happens

Trigger: POST /api/ai/agent/saveSession for a session whose session.json was truncated by a crash/power-loss mid-write, hand-edited and broken, mangled by a third-party sync tool (Dropbox/OneDrive partial sync), or written by an incompatible older/newer version.

Common situations: Hard kill during agent turn commit; workspace synced through a cloud drive that produced a conflict file or zero-length file; users editing session.json manually to rename or prune history; disk-level corruption.

Related errors


AI-assisted analysis of siyuan-note/siyuan@afa823b6b4 (2026-08-18). Data as JSON: /api/errors/091d88f4331a3d78. Report an issue: GitHub.