Skip to content

fix: prune stale session entries, cap entry count, and rotate sessions.json#12920

Closed
skyfallsin wants to merge 1 commit intoopenclaw:mainfrom
skyfallsin:fix/session-store-pruning-and-rotation
Closed

fix: prune stale session entries, cap entry count, and rotate sessions.json#12920
skyfallsin wants to merge 1 commit intoopenclaw:mainfrom
skyfallsin:fix/session-store-pruning-and-rotation

Conversation

@skyfallsin
Copy link
Copy Markdown
Contributor

@skyfallsin skyfallsin commented Feb 9, 2026

Problem

sessions.json grows unbounded over time. Every heartbeat tick (default: 30m) triggers multiple full rewrites, and session keys from groups, threads, and DMs accumulate indefinitely with large embedded objects (skillsSnapshot, systemPromptReport). At >50MB the synchronous JSON parse blocks the event loop, causing Telegram webhook timeouts and effectively taking the bot down.

Fix

Three mitigations, all running inside saveSessionStoreUnlocked() on every write:

1. Prune stale entries

Remove entries with updatedAt older than 30 days. Entries without updatedAt are preserved (cannot determine staleness).

2. Cap entry count

Keep only the 500 most recently updated entries. Entries without updatedAt are evicted first (lowest priority).

3. File rotation

If the existing sessions.json exceeds 10MB before a write, rename it to sessions.json.bak.{timestamp}. Only the 3 most recent backups are kept.

Configuration

All three thresholds are configurable via openclaw.json under session.maintenance:

{
  "session": {
    "maintenance": {
      "pruneDays": 30,
      "maxEntries": 500,
      "rotateBytes": 10485760
    }
  }
}

All fields are optional — built-in defaults apply when unset. No env vars are used; configuration is purely through openclaw.json.

The SessionMaintenanceConfig type is added to types.base.ts and the Zod schema is added to zod-schema.session.ts.

The three exported functions also accept an optional override parameter for direct use in tests or programmatic callers:

  • pruneStaleEntries(store, overrideDays?)
  • capEntryCount(store, overrideMax?)
  • rotateSessionFile(storePath, overrideBytes?)

Changes

File Change
src/config/sessions/store.ts Three exported functions, resolveMaintenanceConfig() reads from openclaw.json, wired into saveSessionStoreUnlocked()
src/config/sessions/store.pruning.test.ts 25 new tests across 4 describe blocks (unit + integration). Unit tests use override params; integration tests mock loadConfig to control config
src/config/types.base.ts SessionMaintenanceConfig type definition
src/config/zod-schema.session.ts maintenance field added to SessionSchema
src/config/sessions.test.ts Updated 4 existing tests to use Date.now() instead of epoch-relative timestamps

Test results

72 tests pass (7 test files).

Notes

  • Pruning and capping mutate the store object in-place before serialization. File rotation renames the on-disk file before the new write.
  • No changes to function signatures of existing exported functions.
  • Logged via createSubsystemLogger("sessions/store") at info level when entries are pruned, capped, or the file is rotated.

Greptile Overview

Greptile Summary

This PR adds automatic sessions.json maintenance during session store writes: pruning entries older than a configurable age, capping total entries to a configurable maximum, and rotating the on-disk file when it exceeds a size threshold (keeping a small set of .bak.{timestamp} backups). Configuration is wired through openclaw.json under session.maintenance with a new SessionMaintenanceConfig type and Zod validation. Migrations are updated to be able to bypass maintenance during one-time legacy merges.

One issue to fix before merge: the new pruning/capping/rotation test suite references crypto.randomUUID() without importing crypto, which will break in environments where crypto is not a global in tests.

Confidence Score: 4/5

  • This PR is close to safe to merge, with one clear test/runtime issue to fix.
  • Core maintenance logic (pruning/capping/rotation) is internally consistent and call sites remain compatible via an optional options param, but the new test file relies on an undeclared crypto identifier which can cause CI failures depending on the test runtime globals.
  • src/config/sessions/store.pruning.test.ts

(2/5) Greptile learns from your feedback when you react with thumbs up/down!

@skyfallsin skyfallsin force-pushed the fix/session-store-pruning-and-rotation branch 4 times, most recently from 306a3d1 to 4f31c6f Compare February 9, 2026 23:15
@Takhoffman Takhoffman self-assigned this Feb 9, 2026
@skyfallsin skyfallsin force-pushed the fix/session-store-pruning-and-rotation branch 2 times, most recently from 36d7737 to 21b8849 Compare February 9, 2026 23:55
@skyfallsin skyfallsin marked this pull request as ready for review February 9, 2026 23:55
Copy link
Copy Markdown
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 file reviewed, 1 comment

Edit Code Review Agent Settings | Greptile

…s.json

The sessions.json file grows unbounded over time. Every heartbeat tick (default: 30m)
triggers multiple full rewrites, and session keys from groups, threads, and DMs
accumulate indefinitely with large embedded objects (skillsSnapshot,
systemPromptReport). At >50MB the synchronous JSON parse blocks the event loop,
causing Telegram webhook timeouts and effectively taking the bot down.

Three mitigations, all running inside saveSessionStoreUnlocked() on every write:

1. Prune stale entries: remove entries with updatedAt older than 30 days
   (configurable via session.maintenance.pruneDays in openclaw.json)

2. Cap entry count: keep only the 500 most recently updated entries
   (configurable via session.maintenance.maxEntries). Entries without updatedAt
   are evicted first.

3. File rotation: if the existing sessions.json exceeds 10MB before a write,
   rename it to sessions.json.bak.{timestamp} and keep only the 3 most recent
   backups (configurable via session.maintenance.rotateBytes).

All three thresholds are configurable under session.maintenance in openclaw.json
with Zod validation. No env vars.

Existing tests updated to use Date.now() instead of epoch-relative timestamps
(1, 2, 3) that would be incorrectly pruned as stale.

27 new tests covering pruning, capping, rotation, and integration scenarios.
@skyfallsin skyfallsin force-pushed the fix/session-store-pruning-and-rotation branch from 21b8849 to c26c0b2 Compare February 10, 2026 00:01
@skyfallsin
Copy link
Copy Markdown
Contributor Author

greptile feedback addressed

@gumadeiras
Copy link
Copy Markdown
Member

Fix landed on main via e19a235.

Thanks @skyfallsin!

@gumadeiras gumadeiras closed this Feb 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants