siyuan-note/siyuan · error

save session file failed: %w

Error message

save session file failed: %w

What it means

The actual persistence step of SaveSessionState: filelock.WriteFile atomically writes session.json under the session directory (kernel/agent/session.go:417-418). Any failure is wrapped as 'save session file failed'. The directory already exists at this point, so failures are write-level: disk full, permission on the file itself, or the cross-process file lock being unavailable.

Source

Thrown at kernel/agent/session.go:418

			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"
	}
	createdAt := meta.CreatedAt
	if value := numberToInt64(newData["createdAt"]); value > 0 {
		createdAt = value
	}
	updatedAt := meta.UpdatedAt
	if value := numberToInt64(newData["updatedAt"]); value > 0 {
		updatedAt = value

View on GitHub (pinned to afa823b6b4)

Solutions

  1. Free disk space / fix ownership of session.json, then re-run the save from a freshly loaded session (revision may have advanced)
  2. Delete a stale filelock lock file under the workspace if no other kernel instance is running
  3. On Windows, exclude the workspace from antivirus real-time scanning
  4. Check kernel logs — a failure here after a successful write of runtime state means the turn commit needs to be redone with the same commitTurnID
Defensive patterns

Strategy: retry

Validate before calling

// Pre-flight: confirm space and file writability before long agent turns
// df -h <workspace> && test -w <workspace>/data/storage/ai/agent/sessions/<id>/session.json

Type guard

null

Try / catch

catch (e) {
  if (/save session file failed/.test(e?.data?.msg ?? '')) {
    // fix disk/lock cause; re-GET the session for the latest revision; retry the save once
  }
}

Prevention

When it happens

Trigger: POST /api/ai/agent/saveSession when the disk is full (ENOSPC), session.json is read-only or owned by another user (EACCES), a stale lock file blocks filelock on Windows, or the filesystem is transient (network volume dropped). Note the revision was already computed — the caller must re-load before retrying.

Common situations: Full disk during long agent sessions; antivirus/indexer holding the file on Windows; two SiYuan instances on the same workspace without the sync-detection noticing; flaky removable storage.

Related errors


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