[Skip to content](https://github.com/NousResearch/hermes-agent/issues/3015#start-of-content)

You signed in with another tab or window. [Reload](https://github.com/NousResearch/hermes-agent/issues/3015) to refresh your session.You signed out in another tab or window. [Reload](https://github.com/NousResearch/hermes-agent/issues/3015) to refresh your session.You switched accounts on another tab or window. [Reload](https://github.com/NousResearch/hermes-agent/issues/3015) to refresh your session.Dismiss alert

{{ message }}

### Uh oh!

There was an error while loading. [Please reload this page](https://github.com/NousResearch/hermes-agent/issues/3015).

[NousResearch](https://github.com/NousResearch)/ **[hermes-agent](https://github.com/NousResearch/hermes-agent)** Public

- [Notifications](https://github.com/login?return_to=%2FNousResearch%2Fhermes-agent) You must be signed in to change notification settings
- [Fork\\
38.3k](https://github.com/login?return_to=%2FNousResearch%2Fhermes-agent)
- [Star\\
210k](https://github.com/login?return_to=%2FNousResearch%2Fhermes-agent)


# Session File Leak: JSON sessions never deleted, causing unbounded disk growth and cost explosion\#3015

[New issue](https://github.com/login?return_to=https://github.com/NousResearch/hermes-agent/issues/3015)

Copy link

[New issue](https://github.com/login?return_to=https://github.com/NousResearch/hermes-agent/issues/3015)

Copy link

Closed

[#16286](https://github.com/NousResearch/hermes-agent/pull/16286)

Closed

[Session File Leak: JSON sessions never deleted, causing unbounded disk growth and cost explosion](https://github.com/NousResearch/hermes-agent/issues/3015#top)#3015

[#16286](https://github.com/NousResearch/hermes-agent/pull/16286)

Copy link

## Description

[![@rustyorb](https://avatars.githubusercontent.com/u/111198602?u=dc91d9a20050ae0e1d25cf1ec9b19060b7820507&v=4&size=48)](https://github.com/rustyorb)

[rustyorb](https://github.com/rustyorb)

opened [on Mar 25on Mar 25, 2026](https://github.com/NousResearch/hermes-agent/issues/3015#issue-4137994045)

Last edited by rustyorb

Issue body actions

# Session File Leak: JSON Sessions Never Deleted, Causing Unbounded Disk Growth and Cost Explosion

## Summary

Hermes Agent creates session JSON files for every conversation (chat, cron, subagent) but **never deletes them**. Over time, this leads to:

- Hundreds/thousands of accumulated session files (observed: 728 files, 94MB on single machine)
- Unbounded disk usage
- Exacerbated token costs (large sessions with 500+ messages accumulate context forever)
- 22M+ tokens burned in ~2 hours due to unchecked session growth

## Root Cause Analysis

### 1\. Session Reset Creates New Files, Never Deletes Old

In `gateway/session.py`, the `reset_session()` function:

1. Creates a **new**`SessionEntry` with a **new**`session_id`
2. Updates the in-memory mapping (`_entries`) to point to the new session
3. Saves to session store
4. Calls `end_session()` in SQLite database (marks as ended)
5. **Does NOT delete the old JSON file**

```
# From session.py:776-800
def reset_session(self, session_key: str) -> Optional[SessionEntry]:
    # ... creates new session_id ...
    new_entry = SessionEntry(
        session_key=session_key,
        session_id=session_id,  # NEW ID
        # ...
    )
    self._entries[session_key] = new_entry  # Updates mapping
    self._save()
    # Creates NEW session file, old one remains on disk forever
```

### 2\. Cron Sessions Multiply Rapidly

Each cron execution creates a new session with ID format: `cron_{job_id}_{timestamp}`. With crons running every 15-45 minutes:

- 1 cron × 96 runs/day × 7 days = 672 session files per week per cron
- Observed: 535 cron session files (73.5% of all sessions)

### 3\. Session Files Are Never Cleaned Up

**No deletion mechanism exists:**

- `end_session()` in `hermes_state.py:252` only marks sessions as ended in SQLite (`ended_at`, `end_reason`)
- No `os.remove()` call for session JSON files
- No retention policy for filesystem cleanup
- Sessions accumulate indefinitely

### 4\. Large Sessions Retain Full Context

Session files contain complete message history. Observed:

- Sessions with 500-600 messages
- File sizes: 500KB-1MB each
- All tool call results preserved forever
- Context compression happens in-memory but original files remain

## Impact

### Quantified on SCANNINGPC (Deimos):

- **728 session files** accumulated
- **94.33 MB** total size
- **535 cron sessions** (73.5% of total)
- **418 sessions** aged 1-7 days
- **245 sessions** aged 6-24 hours

### Token Cost Explosion

One session accumulated **602 messages in 107 seconds**:

- 209 tool call iterations
- ~102K tokens
- 5.61 messages/second processing rate
- User sent 105 messages (frustrated rapid-fire interaction)
- Assistant responded with 274 messages + 223 tool results

**With no session cleanup, context grows exponentially until iteration limit hit.**

## Reproduction Steps

1. Run Hermes gateway with any cron job (e.g., every 15 minutes)

2. Let run for 24-48 hours

3. Check `~/.hermes/sessions/`:



```
ls -la ~/.hermes/sessions/session_*.json | wc -l
# Returns: 100+ files
```

4. Observe files are never deleted, only new ones created


## Evidence

### Session Age Distribution

```
<1h:     9 files
1-6h:    54 files
6-24h:   245 files
1-7d:    418 files
>7d:     2 files
```

### Cron Session Dominance

- Cron sessions: 535 (73.5%)
- Chat sessions: 193 (26.5%)

### Large Session Examples

- `session_20260325_112518_6995d1.json`: 725 KB, 602 messages
- `session_20260325_110422_70aca0.json`: 722 KB, 570 messages

## Proposed Solutions

### Immediate (Hotfix)

1. **Add session file cleanup on reset:**



```
# In reset_session(), before creating new entry:
old_session_file = self._sessions_dir / f"{old_entry.session_id}.json"
if old_session_file.exists():
       old_session_file.unlink()
```

2. **Add retention policy:**
   - Delete session files older than 7 days
   - Keep SQLite records for history
   - Configurable retention period

### Short-term

1. **Session size limits:**
   - Max 1000 messages per session before forced reset
   - Max 10MB per session file
   - Alert when sessions grow too large
2. **Cron session optimization:**
   - Option for cron sessions to not persist to JSON (SQLite only)
   - Automatic cleanup of cron sessions after delivery

### Long-term

1. **Session archival:**
   - Compress old sessions to `.gz`
   - Archive to separate directory
   - Background cleanup job
2. **Cost controls:**
   - Per-session token budgets
   - Max iterations enforced strictly
   - Automatic session kill on threshold breach

## Files Affected

- `gateway/session.py` \- Session lifecycle management
- `hermes_state.py` \- SQLite session tracking
- `run_agent.py` \- Session creation
- `cron/scheduler.py` \- Cron session creation

## Environment

- Hermes Agent version: Latest (as of 2026-03-25)
- Installation: Source (git clone)
- Platforms: Telegram, Discord, CLI, Cron
- OS: Ubuntu (WSL2)

## Labels

`bug`, `performance`, `cost`, `storage`, `sessions`, `priority-critical`

* * *

👍React with 👍1xraysight

## Activity

[Next](https://github.com/NousResearch/hermes-agent/issues/3015?timeline_page=1)

[![](https://avatars.githubusercontent.com/u/53251494?s=64&u=d6ffba7649101b7a500630584a7882dea045338c&v=4)crazywriter1](https://github.com/crazywriter1)

added a commit that references this issue [on Mar 25on Mar 25, 2026](https://github.com/NousResearch/hermes-agent/issues/3015#event-23905698438)

[fix(gateway): delete legacy transcript files on session reset](https://github.com/crazywriter1/hermes-agent/commit/83ccc038529373c79d0ba54bfdbfc988058daf43)

...

[83ccc03](https://github.com/crazywriter1/hermes-agent/commit/83ccc038529373c79d0ba54bfdbfc988058daf43)

[![](https://avatars.githubusercontent.com/u/53251494?s=64&u=d6ffba7649101b7a500630584a7882dea045338c&v=4)crazywriter1](https://github.com/crazywriter1)

mentioned this [on Mar 25on Mar 25, 2026](https://github.com/NousResearch/hermes-agent/issues/3015#event-5273189308)

- [fix(gateway): delete legacy session transcript files on reset (#3015) #3028](https://github.com/NousResearch/hermes-agent/pull/3028)


### crazywriter1 commented on Mar 25on Mar 25, 2026

[![@crazywriter1](https://avatars.githubusercontent.com/u/53251494?u=d6ffba7649101b7a500630584a7882dea045338c&v=4&size=48)](https://github.com/crazywriter1)

[crazywriter1](https://github.com/crazywriter1)

[on Mar 25on Mar 25, 2026](https://github.com/NousResearch/hermes-agent/issues/3015#issuecomment-4129443890)

Last edited by crazywriter1

Contributor

More actions

Thanks for the thorough report. Opened [#3028](https://github.com/NousResearch/hermes-agent/pull/3028): We now delete `{session_id}.jsonl` (and `.json` if present) under `sessions_dir` when a session is reset or auto-reset by policy; SQLite is unchanged. `/resume` paths still keep old files on purpose.

Feedback welcome.

### rustyorb commented on Mar 25on Mar 25, 2026

[![@rustyorb](https://avatars.githubusercontent.com/u/111198602?u=dc91d9a20050ae0e1d25cf1ec9b19060b7820507&v=4&size=48)](https://github.com/rustyorb)

[rustyorb](https://github.com/rustyorb)

[on Mar 25on Mar 25, 2026](https://github.com/NousResearch/hermes-agent/issues/3015#issuecomment-4129487628)

Author

More actions

[@crazywriter1](https://github.com/crazywriter1) Thanks for the quick turnaround on this. The fix in [#3028](https://github.com/NousResearch/hermes-agent/pull/3028) directly addresses the unbounded growth we observed—728 files accumulating on one machine, 1,362 on another.

Confirmed the approach handles both manual resets and auto-resets (idle/daily), which covers the cron proliferation we identified (535 cron session files, 73% of total).

Good call keeping `/resume` sessions intact. That's a sensible distinction—users explicitly opting to preserve history vs. automatic cleanup on reset.

The retention sweeps mentioned as out-of-scope would be a nice follow-up (7-day default, configurable), but this PR solves the immediate leak. Will test once merged and report back if any edge cases surface.

Appreciate the thorough test coverage too.

👍React with 👍1crazywriter1

### ravishrawal commented on Mar 26on Mar 26, 2026

[![@ravishrawal](https://avatars.githubusercontent.com/u/26351259?v=4&size=48)](https://github.com/ravishrawal)

[ravishrawal](https://github.com/ravishrawal)

[on Mar 26on Mar 26, 2026](https://github.com/NousResearch/hermes-agent/issues/3015#issuecomment-4132337547)

More actions

Hey all I'm seeing this with my telegram set up as well - all messages & tasks via the telegram chat stack into the same session ID and send all historical messages every time leading to the triangular sum of messages being sent each time, even if there was hours of inactivity. What is the right way to auto-reset session after a task is done?

### SeeYangZhi commented on Apr 9on Apr 9, 2026

[![@SeeYangZhi](https://avatars.githubusercontent.com/u/46666370?u=a4b29b62ade91f8aa7e2a898d71508bf7dca55ac&v=4&size=48)](https://github.com/SeeYangZhi)

[SeeYangZhi](https://github.com/SeeYangZhi)

[on Apr 9on Apr 9, 2026](https://github.com/NousResearch/hermes-agent/issues/3015#issuecomment-4214375588)

Contributor

More actions

Adding a data point from another machine and a finding that strengthens the case for deleting JSON files on session end.

**Observation:**`session_search` reads entirely from SQLite FTS5 (`state.db`), not from the JSON files. The JSON transcripts in `~/.hermes/sessions/` are only used by the gateway for mid-session persistence (surviving restarts). Once a session ends and messages are committed to SQLite, the JSON files serve no purpose.

This means the proposed fix (delete JSONs on reset/end) has zero risk of breaking `session_search` or any other feature.

**Numbers from my machine (macOS, 3 days of use):**

- 340 JSON session files, 82MB total (~246KB avg)
- SQLite `state.db`: 28MB with 153 sessions, 9,761 messages, FTS5 indexed
- Growth rate: ~113 sessions/day (mix of CLI, Telegram, cron, subagents)
- Projected: ~10GB/year if unchecked

**`prune_sessions()` gap:**`hermes_state.py:1264` has a working `prune_sessions(older_than_days)` and it's exposed via `hermes sessions prune`, but it only deletes SQLite records — it doesn't touch the JSON files on disk. So even users who know about the prune command still accumulate JSON files forever.

Workaround until this is fixed upstream:

```
find ~/.hermes/sessions/ -name '*.json' -mtime +7 -delete
```

[![](https://avatars.githubusercontent.com/u/46666370?s=64&u=a4b29b62ade91f8aa7e2a898d71508bf7dca55ac&v=4)SeeYangZhi](https://github.com/SeeYangZhi)

added a commit that references this issue [on Apr 9on Apr 9, 2026](https://github.com/NousResearch/hermes-agent/issues/3015#event-24336567009)

[fix(sessions): delete on-disk transcript files during prune and delete (](https://github.com/SeeYangZhi/hermes-agent/commit/f663a74f094ce7502033c46ffa3e524cb6763093)

...

[f663a74](https://github.com/SeeYangZhi/hermes-agent/commit/f663a74f094ce7502033c46ffa3e524cb6763093)

[![](https://avatars.githubusercontent.com/u/46666370?s=64&u=a4b29b62ade91f8aa7e2a898d71508bf7dca55ac&v=4)SeeYangZhi](https://github.com/SeeYangZhi)

mentioned this [on Apr 9on Apr 9, 2026](https://github.com/NousResearch/hermes-agent/issues/3015#event-5885785383)

- [fix(sessions): delete on-disk transcript files during prune and delete #6613](https://github.com/NousResearch/hermes-agent/pull/6613)


### iRonin commented on Apr 14on Apr 14, 2026

[![@iRonin](https://avatars.githubusercontent.com/u/27929?v=4&size=48)](https://github.com/iRonin)

[iRonin](https://github.com/iRonin)

[on Apr 14on Apr 14, 2026](https://github.com/NousResearch/hermes-agent/issues/3015#issuecomment-4243355560)

Contributor

More actions

## Analysis and suggested implementation

I audited the codebase and confirmed the issue. Two additional observations beyond what's already described:

### 1\. The `_entries` dict in `SessionStore` is also never pruned

`gateway/session.py:507` maintains `self._entries: Dict[str, SessionEntry] = {}` which grows with every unique `session_key`. Entries are replaced on session reset but old keys are never removed. For single-user setups this is bounded, but multi-user gateways will see linear growth.

### 2\. SQLite `messages` table has no retention policy either

`hermes_state.py` stores every message permanently. Even with session JSON cleanup, the SQLite database grows unbounded. The `ended_at` field on sessions exists but no cleanup job uses it.

### Suggested implementation priority

**P1 — Cron session cleanup (highest ROI)**

Cron sessions dominate file count (73.5% in the reported case). Add a `delete_session_files()` method to `SessionDB` that removes JSON files for sessions where `source='cron'` AND `ended_at < now - retention_days`. Run this at gateway startup and after each cron delivery.

**P2 — Session file deletion on reset**

In `SessionStore.reset_session()`, add:

````
old_file = self.sessions_dir / f"{old_entry.session_id}.json"\nif old_file.exists():\n    old_file.unlink()\n```

**P3 -- Background retention job**
A periodic task (every 24h) that walks `sessions_dir` and removes files older than `config.session_retention_days` (default 7). Keep SQLite records for historical search — only delete the JSON files.

**P4 — `_entries` pruning**
After session reset, remove the old key from `_entries`. Add a `max_entries` config (default 500) with LRU eviction when exceeded.

Want me to open a PR for P1+P2? Those two would eliminate ~80% of the reported accumulation with minimal risk.
````

[![](https://avatars.githubusercontent.com/u/127238744?s=64&u=b17ed1d4d68a017b076db1f674491d2896d6076d&v=4)teknium1](https://github.com/teknium1)

mentioned this in 2 pull requests [on Apr 26on Apr 26, 2026](https://github.com/NousResearch/hermes-agent/issues/3015#event-6150514752)

- [feat(state): auto-prune old sessions + VACUUM state.db at startup #13861](https://github.com/NousResearch/hermes-agent/pull/13861)

- [fix(sessions): delete transcript files during auto-prune (salvage #6613, closes #3015) #16286](https://github.com/NousResearch/hermes-agent/pull/16286)


[![](https://avatars.githubusercontent.com/u/127238744?s=64&u=b17ed1d4d68a017b076db1f674491d2896d6076d&v=4)teknium1](https://github.com/teknium1)

added a commit that references this issue [on Apr 26on Apr 26, 2026](https://github.com/NousResearch/hermes-agent/issues/3015#event-24887696166)

[fix(sessions): delete on-disk transcript files during prune and delete (](https://github.com/NousResearch/hermes-agent/commit/3b60abb6bb7eb6ae50f8c51927f5cfac1deddde7)

...

[3b60abb](https://github.com/NousResearch/hermes-agent/commit/3b60abb6bb7eb6ae50f8c51927f5cfac1deddde7)

[![](https://avatars.githubusercontent.com/u/127238744?s=64&u=b17ed1d4d68a017b076db1f674491d2896d6076d&v=4)teknium1](https://github.com/teknium1)

closed this as [completed](https://github.com/NousResearch/hermes-agent/issues?q=is%3Aissue%20state%3Aclosed%20archived%3Afalse%20reason%3Acompleted) in [#16286](https://github.com/NousResearch/hermes-agent/pull/16286) [on Apr 26on Apr 26, 2026](https://github.com/NousResearch/hermes-agent/issues/3015#event-24887696182)

[![](https://avatars.githubusercontent.com/u/127238744?s=64&u=b17ed1d4d68a017b076db1f674491d2896d6076d&v=4)teknium1](https://github.com/teknium1)

mentioned this [on Apr 26on Apr 26, 2026](https://github.com/NousResearch/hermes-agent/issues/3015#event-6228213836)

- [feat(checkpoints): auto-prune orphan and stale shadow repos at startup #16303](https://github.com/NousResearch/hermes-agent/pull/16303)


[![](https://avatars.githubusercontent.com/u/127238744?s=64&u=b17ed1d4d68a017b076db1f674491d2896d6076d&v=4)teknium1](https://github.com/teknium1)

added a commit that references this issue [on Apr 26on Apr 26, 2026](https://github.com/NousResearch/hermes-agent/issues/3015#event-24888219850)

[feat(checkpoints): auto-prune orphan and stale shadow repos at startup](https://github.com/NousResearch/hermes-agent/commit/ecae59940710ee87a792095e1c698bf6c024c0da)

...

Verified [ecae599](https://github.com/NousResearch/hermes-agent/commit/ecae59940710ee87a792095e1c698bf6c024c0da)

### 26 remaining items

Load more

Loading

[Sign up for free](https://github.com/signup?return_to=https://github.com/NousResearch/hermes-agent/issues/3015)**to join this conversation on GitHub.** Already have an account? [Sign in to comment](https://github.com/login?return_to=https://github.com/NousResearch/hermes-agent/issues/3015)

## Metadata

## Metadata

### Assignees

No one assigned

### Labels

No labels

No labels

### Type

No type

### Fields

No fields configured for issues without a type.

### Projects

No projects

### Milestone

No milestone

### Relationships

None yet

### Development

- [fix(gateway): delete legacy session transcript files on reset (#3015)NousResearch/hermes-agent](https://github.com/NousResearch/hermes-agent/pull/3028)
- [fix(sessions): delete on-disk transcript files during prune and deleteNousResearch/hermes-agent](https://github.com/NousResearch/hermes-agent/pull/6613)
- [fix(sessions): delete transcript files during auto-prune (salvage #6613, closes #3015)NousResearch/hermes-agent](https://github.com/NousResearch/hermes-agent/pull/16286) [Releasev2026.4.30](https://github.com/NousResearch/hermes-agent/releases/tag/v2026.4.30)

### Participants

[![@iRonin](https://avatars.githubusercontent.com/u/27929?s=64&v=4)](https://github.com/iRonin)[![@ravishrawal](https://avatars.githubusercontent.com/u/26351259?s=64&v=4)](https://github.com/ravishrawal)[![@SeeYangZhi](https://avatars.githubusercontent.com/u/46666370?s=64&u=a4b29b62ade91f8aa7e2a898d71508bf7dca55ac&v=4)](https://github.com/SeeYangZhi)[![@crazywriter1](https://avatars.githubusercontent.com/u/53251494?s=64&u=d6ffba7649101b7a500630584a7882dea045338c&v=4)](https://github.com/crazywriter1)[![@rustyorb](https://avatars.githubusercontent.com/u/111198602?s=64&u=dc91d9a20050ae0e1d25cf1ec9b19060b7820507&v=4)](https://github.com/rustyorb)

## Issue actions

- ![](https://github.githubassets.com/assets/github-copilot-app-light-7138e992c731a2bb.png)Open in GitHub Copilot app

You can’t perform that action at this time.