openclaw - 💡(How to fix) Fix [Bug]: sessions cleanup --enforce silently no-ops on cap-overflow / prune-stale when no --fix-missing or --fix-dm-scope mutation (beta.4 regression)

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…

On 2026.5.16-beta.4, openclaw sessions cleanup --enforce reports "Applied maintenance" but does not actually apply cap-overflow or prune-stale actions unless --fix-missing or --fix-dm-scope also produces a real mutation. The dry-run output for the same agent correctly plans the cap-overflow or prune-stale work; the apply path silently no-ops. The file on disk is not rewritten (mtime unchanged) even though the CLI prints Applied maintenance. Current entries: <unchanged count>.

This is a regression that lines up with the fix for #81749 (PR #81687, commit 36755e405754, merged 2026-05-14). That fix correctly narrowed the override so it no longer bypasses the user's session.maintenance config, but it also appears to have made the enforcement path dependent on the inline callback in runSessionsCleanup returning a non-zero mutation count. When the only requested work is cap-overflow or prune-stale (and the user did not pass --fix-missing / --fix-dm-scope), the callback returns zero, updateSessionStore skips the write, the internal maintenance pass that would otherwise apply pruneStaleEntries and capEntryCount is never reached, and onMaintenanceApplied is never invoked. The summary fallback still sets applied: true, so the CLI reports success.

Error Message

A regression test that runs sessions cleanup --enforce against a store that exceeds maxEntries (with no missing transcripts and no dm-scope mismatch) would catch this. The existing store.pruning.integration.test.ts covers warn-mode skipping enforcement but does not cover this enforce-mode-applies path through the standalone CLI.

Root Cause

On 2026.5.16-beta.4, openclaw sessions cleanup --enforce reports "Applied maintenance" but does not actually apply cap-overflow or prune-stale actions unless --fix-missing or --fix-dm-scope also produces a real mutation. The dry-run output for the same agent correctly plans the cap-overflow or prune-stale work; the apply path silently no-ops. The file on disk is not rewritten (mtime unchanged) even though the CLI prints Applied maintenance. Current entries: <unchanged count>.

This is a regression that lines up with the fix for #81749 (PR #81687, commit 36755e405754, merged 2026-05-14). That fix correctly narrowed the override so it no longer bypasses the user's session.maintenance config, but it also appears to have made the enforcement path dependent on the inline callback in runSessionsCleanup returning a non-zero mutation count. When the only requested work is cap-overflow or prune-stale (and the user did not pass --fix-missing / --fix-dm-scope), the callback returns zero, updateSessionStore skips the write, the internal maintenance pass that would otherwise apply pruneStaleEntries and capEntryCount is never reached, and onMaintenanceApplied is never invoked. The summary fallback still sets applied: true, so the CLI reports success.

Fix Action

Workaround

Edit sessions.json directly to remove the offending key, optionally also renaming the associated transcript files. This is what I had to do to bring an over-cap store back under the configured maxEntries.

The internal maintenance pass does still fire correctly during normal agent activity (when sessions are written for real), so the policy holds in steady state. The bug is specific to the one-shot CLI.

Code Example

"session": {
     "maintenance": {
       "mode": "enforce",
       "pruneAfter": "14d",
       "maxEntries": 20,
       "maxDiskBytes": "500mb",
       "resetArchiveRetention": "14d"
     }
   }

---

Session store: /path/to/sessions.json
   Applied maintenance. Current entries: 21

---

$ openclaw sessions cleanup --agent wren --enforce --dry-run
Session store: /home/hopewell/.openclaw/agents/wren/sessions/sessions.json
Maintenance mode: enforce
Entries: 21 -> 20 (remove 1)
Would prune missing transcripts: 0
Would retire stale direct DM sessions: 0
Would prune stale: 0
Would cap overflow: 1
Would prune unreferenced artifacts: 0

Planned session actions:
Action           Key                        Age       Model          Flags
... (18 keep lines elided) ...
cap-overflow     agent:wren:subag...2f3a8a  40h ago   claude-opus-4-7 aborted id:497559f7-43b5-4f31-adf3-cc3150f59420

$ stat -c '%y %s' /home/hopewell/.openclaw/agents/wren/sessions/sessions.json
2026-05-17 13:36:49.321945457 +0000 35816

$ openclaw sessions cleanup --agent wren --enforce
Session store: /home/hopewell/.openclaw/agents/wren/sessions/sessions.json
Applied maintenance. Current entries: 21

$ stat -c '%y %s' /home/hopewell/.openclaw/agents/wren/sessions/sessions.json
2026-05-17 13:36:49.321945457 +0000 35816

$ python3 -c "import json; print(len(json.load(open('/home/hopewell/.openclaw/agents/wren/sessions/sessions.json'))))"
21
RAW_BUFFERClick to expand / collapse

Bug type

Behavior bug (incorrect output/state without crash)

Beta release blocker

No

Summary

On 2026.5.16-beta.4, openclaw sessions cleanup --enforce reports "Applied maintenance" but does not actually apply cap-overflow or prune-stale actions unless --fix-missing or --fix-dm-scope also produces a real mutation. The dry-run output for the same agent correctly plans the cap-overflow or prune-stale work; the apply path silently no-ops. The file on disk is not rewritten (mtime unchanged) even though the CLI prints Applied maintenance. Current entries: <unchanged count>.

This is a regression that lines up with the fix for #81749 (PR #81687, commit 36755e405754, merged 2026-05-14). That fix correctly narrowed the override so it no longer bypasses the user's session.maintenance config, but it also appears to have made the enforcement path dependent on the inline callback in runSessionsCleanup returning a non-zero mutation count. When the only requested work is cap-overflow or prune-stale (and the user did not pass --fix-missing / --fix-dm-scope), the callback returns zero, updateSessionStore skips the write, the internal maintenance pass that would otherwise apply pruneStaleEntries and capEntryCount is never reached, and onMaintenanceApplied is never invoked. The summary fallback still sets applied: true, so the CLI reports success.

Steps to reproduce

  1. Configure session.maintenance in openclaw.json:
    "session": {
      "maintenance": {
        "mode": "enforce",
        "pruneAfter": "14d",
        "maxEntries": 20,
        "maxDiskBytes": "500mb",
        "resetArchiveRetention": "14d"
      }
    }
  2. Build a sessions store that exceeds maxEntries (in my case, agent had 21 entries against a 20-entry cap).
  3. Run openclaw sessions cleanup --agent <name> --enforce --dry-run. The plan correctly identifies one entry as cap-overflow and reports Entries: 21 -> 20 (remove 1) and Would cap overflow: 1.
  4. Run openclaw sessions cleanup --agent <name> --enforce. Output:
    Session store: /path/to/sessions.json
    Applied maintenance. Current entries: 21
  5. stat the sessions.json file. mtime is unchanged. The entry was not removed.
  6. Attempting the same command with --fix-missing --enforce also fails when no transcripts are missing (the callback still returns 0).

Expected behavior

openclaw sessions cleanup --enforce should apply all maintenance actions identified during the preview phase, including cap-overflow and prune-stale, regardless of whether --fix-missing or --fix-dm-scope is set.

Actual behavior

The apply path silently no-ops for cap-overflow and prune-stale unless an unrelated --fix-* flag also performs a real store mutation. The CLI prints Applied maintenance but the sessions.json file is not modified. The internal maintenance pass that would handle cap/prune is only triggered as a side effect of updateSessionStore writing the store, and updateSessionStore only writes when the inline callback returns a non-zero removal count.

Code trace

In dist/sessions-biOdhq5o.js on 2026.5.16-beta.4 (which corresponds to src/config/sessions/cleanup-service.ts on main):

  • previewStoreCleanup (around line 303 in the bundled dist) runs the full set of policy checks against a cloned store, including pruneStaleEntries and capEntryCount. The dry-run table is built from this preview and includes the correct cap-overflow / prune-stale plan.
  • runSessionsCleanup then iterates targets (line ~434) and for each one calls updateSessionStore(target.storePath, callback, opts).
  • The inline callback (lines 439-460) only runs:
    • pruneMissingTranscriptEntries if opts.fixMissing is set.
    • retireMainScopeDirectSessionEntries if opts.fixDmScope is set.
    • It returns the sum of removed entries.
  • The cap-overflow and prune-stale work is delegated to updateSessionStore's internal maintenance hook, configured via maintenanceOverride: { mode } and onMaintenanceApplied.
  • That internal maintenance pass appears to only fire when the callback mutates the store. With no --fix-missing / --fix-dm-scope mutation, the callback returns 0, the store is not rewritten, and the internal maintenance is skipped. appliedReportRef.current stays null.
  • The summary fallback at line 492 still sets applied: true and appliedCount: Object.keys(afterStore).length, so the CLI reports Applied maintenance with the unchanged count.

Workaround

Edit sessions.json directly to remove the offending key, optionally also renaming the associated transcript files. This is what I had to do to bring an over-cap store back under the configured maxEntries.

The internal maintenance pass does still fire correctly during normal agent activity (when sessions are written for real), so the policy holds in steady state. The bug is specific to the one-shot CLI.

OpenClaw version

2026.5.16-beta.4

Operating system

Linux 6.8.0-111-generic (Ubuntu)

Install method

npm global

Model

openai/gpt-5.5

Provider / routing chain

openai-codex (OAuth) -> codex harness -> openai

Additional provider/model setup details

Not relevant to this bug. The issue is in the session-store cleanup CLI path and is independent of provider, model, or runtime.

Logs, screenshots, and evidence

Reproducer transcript (single agent, cap=20, store has 21 entries):

$ openclaw sessions cleanup --agent wren --enforce --dry-run
Session store: /home/hopewell/.openclaw/agents/wren/sessions/sessions.json
Maintenance mode: enforce
Entries: 21 -> 20 (remove 1)
Would prune missing transcripts: 0
Would retire stale direct DM sessions: 0
Would prune stale: 0
Would cap overflow: 1
Would prune unreferenced artifacts: 0

Planned session actions:
Action           Key                        Age       Model          Flags
... (18 keep lines elided) ...
cap-overflow     agent:wren:subag...2f3a8a  40h ago   claude-opus-4-7 aborted id:497559f7-43b5-4f31-adf3-cc3150f59420

$ stat -c '%y %s' /home/hopewell/.openclaw/agents/wren/sessions/sessions.json
2026-05-17 13:36:49.321945457 +0000 35816

$ openclaw sessions cleanup --agent wren --enforce
Session store: /home/hopewell/.openclaw/agents/wren/sessions/sessions.json
Applied maintenance. Current entries: 21

$ stat -c '%y %s' /home/hopewell/.openclaw/agents/wren/sessions/sessions.json
2026-05-17 13:36:49.321945457 +0000 35816

$ python3 -c "import json; print(len(json.load(open('/home/hopewell/.openclaw/agents/wren/sessions/sessions.json'))))"
21

The same pattern was reproducible with --fix-missing --enforce when no transcripts were missing.

Suggested fix direction

The apply path should run the same maintenance steps the preview path runs, not just the --fix-* callbacks. Either:

  • Run pruneStaleEntries and capEntryCount directly in the inline callback in runSessionsCleanup, so the callback returns a non-zero removal count whenever there is real work to do. This forces updateSessionStore to write and triggers the internal maintenance pass naturally.
  • Or, change updateSessionStore so that the internal maintenance pass runs whenever maintenanceOverride is set, regardless of whether the inline callback returned a mutation.

A regression test that runs sessions cleanup --enforce against a store that exceeds maxEntries (with no missing transcripts and no dm-scope mismatch) would catch this. The existing store.pruning.integration.test.ts covers warn-mode skipping enforcement but does not cover this enforce-mode-applies path through the standalone CLI.

Related

  • #81749 (closed): the prior direction of this bug. The fix in PR #81687 / commit 36755e405754 narrowed the maintenance override to avoid forcing enforce against the user's config, but appears to have introduced this regression where enforce no longer applies through the standalone CLI without an unrelated --fix-* mutation.
  • #69404 (merged): flipped default mode to enforce. Load-path enforcement still works (per that PR), but the standalone sessions cleanup --enforce path described here does not.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

FAQ

Expected behavior

openclaw sessions cleanup --enforce should apply all maintenance actions identified during the preview phase, including cap-overflow and prune-stale, regardless of whether --fix-missing or --fix-dm-scope is set.

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING

openclaw - 💡(How to fix) Fix [Bug]: sessions cleanup --enforce silently no-ops on cap-overflow / prune-stale when no --fix-missing or --fix-dm-scope mutation (beta.4 regression)