openclaw - 💡(How to fix) Fix Protected external conversation sessions can bypass maintenance caps and keep large artifacts alive

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…

session.maintenance protects durable external conversation sessions such as Slack channel and thread sessions from normal prune, cap, and disk-budget eviction. This avoids breaking routing continuity, but it also means Slack-heavy OpenClaw instances can accumulate hundreds or thousands of protected session entries and referenced transcripts/artifacts even when session.maintenance.maxEntries and pruneAfter are configured.

This creates an architectural mismatch: the configured maintenance limits imply bounded session storage, but protected external conversation entries are effectively outside those bounds.

This is not being filed as a confirmed cause of the current gateway health warning. A separate investigation suggests the event_loop_utilization,cpu warning in 2026.5.7 can be caused by destructive/short-window health sampling. Session-store growth should be tracked as a separate architectural risk.

Root Cause

The current design conflates two separate needs:

  1. Preserve enough metadata to route future messages/replies correctly.
  2. Preserve the full session entry and its transcript/artifact references indefinitely.

For long-lived Slack workspaces, normal maintenance cannot enforce the configured entry cap or retention window when protected entries dominate. Operators may believe maxEntries and pruneAfter bound session storage, while protected external conversation sessions continue growing.

Large protected stores also increase the cost/risk of session-related operations that load stores, enumerate session directories, compute disk budgets, or hydrate session resources.

Code Example

OpenClaw 2026.5.7 (eeef486)

---

Gateway event loop: degraded reasons=event_loop_utilization,cpu max=0ms p99=0ms util=1 cpu=1.01
Session store (main): .../sessions.json (584 entries)

---

sessions dir size: 1.3G
sessions dir file count: 4874
sessions.json size: 4.33 MiB

---

total entries: 584
unique sessionIds: 584
slack entries: 584
slack channel entries: 17
slack thread entries: 543
protected-looking entries: 584
protected-looking with sessionId: 584
protected-looking slack entries: 584

protected-looking older than 1d: 505
protected-looking older than 7d: 122
protected-looking older than 14d: 0
protected-looking older than 30d: 0
protected-looking slack older than 1d: 505
protected-looking slack older than 7d: 122
protected-looking slack older than 14d: 0
protected-looking slack older than 30d: 0

---

{
  "session": {
    "maintenance": {
      "pruneAfter": "30d",
      "maxEntries": 500,
      "protectedPruneAfter": "90d"
    }
  }
}

---

{
  "session": {
    "maintenance": {
      "externalConversationRetention": "90d"
    }
  }
}
RAW_BUFFERClick to expand / collapse

Summary

session.maintenance protects durable external conversation sessions such as Slack channel and thread sessions from normal prune, cap, and disk-budget eviction. This avoids breaking routing continuity, but it also means Slack-heavy OpenClaw instances can accumulate hundreds or thousands of protected session entries and referenced transcripts/artifacts even when session.maintenance.maxEntries and pruneAfter are configured.

This creates an architectural mismatch: the configured maintenance limits imply bounded session storage, but protected external conversation entries are effectively outside those bounds.

This is not being filed as a confirmed cause of the current gateway health warning. A separate investigation suggests the event_loop_utilization,cpu warning in 2026.5.7 can be caused by destructive/short-window health sampling. Session-store growth should be tracked as a separate architectural risk.

Example From Affected Slack-Heavy Instance

Version:

OpenClaw 2026.5.7 (eeef486)

Health/status context:

Gateway event loop: degraded reasons=event_loop_utilization,cpu max=0ms p99=0ms util=1 cpu=1.01
Session store (main): .../sessions.json (584 entries)

Store/artifact size:

sessions dir size: 1.3G
sessions dir file count: 4874
sessions.json size: 4.33 MiB

Redacted session summary:

total entries: 584
unique sessionIds: 584
slack entries: 584
slack channel entries: 17
slack thread entries: 543
protected-looking entries: 584
protected-looking with sessionId: 584
protected-looking slack entries: 584

protected-looking older than 1d: 505
protected-looking older than 7d: 122
protected-looking older than 14d: 0
protected-looking older than 30d: 0
protected-looking slack older than 1d: 505
protected-looking slack older than 7d: 122
protected-looking slack older than 14d: 0
protected-looking slack older than 30d: 0

Interpretation:

  • All session entries are Slack.
  • All session entries are protected-looking.
  • Most entries are Slack thread sessions.
  • The configured entry cap cannot reduce this store when protected entries dominate.
  • This host does not currently prove 30-day prune bypass by age, because no protected Slack entries are older than 14d. It does demonstrate entry-cap bypass and artifact retention risk.
  • The sessions directory is large relative to sessions.json, suggesting cleanup also needs to reason about referenced transcripts/artifacts, not just entry count.

Code Pointers

Protected session logic:

  • src/config/sessions/store-maintenance.ts
  • isProtectedSessionMaintenanceEntry(...)
  • Thread keys, Telegram topic keys, external group/channel keys, and entries with group/channel/thread metadata are preserved.

Entry cap/prune behavior:

  • src/config/sessions/store-maintenance.ts
  • pruneStaleEntries(...)
  • capEntryCount(...)

Disk-budget behavior:

  • src/config/sessions/disk-budget.ts
  • Protected entries are skipped during budget eviction.
  • Referenced transcripts/artifacts for protected entries are retained.

Slack routing/session key behavior:

  • extensions/slack/src/monitor/message-handler/prepare-routing.ts
  • Slack channel/thread keys are derived from channel id and thread_ts.
  • Old protected entries preserve more than the small routing metadata needed for future delivery.

Why This Matters

The current design conflates two separate needs:

  1. Preserve enough metadata to route future messages/replies correctly.
  2. Preserve the full session entry and its transcript/artifact references indefinitely.

For long-lived Slack workspaces, normal maintenance cannot enforce the configured entry cap or retention window when protected entries dominate. Operators may believe maxEntries and pruneAfter bound session storage, while protected external conversation sessions continue growing.

Large protected stores also increase the cost/risk of session-related operations that load stores, enumerate session directories, compute disk budgets, or hydrate session resources.

Proposed Direction

Add a second-tier lifecycle policy for protected external conversation sessions.

Possible shape:

{
  "session": {
    "maintenance": {
      "pruneAfter": "30d",
      "maxEntries": 500,
      "protectedPruneAfter": "90d"
    }
  }
}

or a more explicit name:

{
  "session": {
    "maintenance": {
      "externalConversationRetention": "90d"
    }
  }
}

Behavior should probably be conservative by default:

  • Keep current protection unless the new policy is explicitly configured.
  • Never prune active sessions, sessions with pending delivery, running state, ACP bindings, quota suspension, or recent activity.
  • Instead of deleting routing continuity outright, trim/archive old protected entries into minimal routing tombstones.
  • Preserve fields needed for delivery/routing, such as deliveryContext, lastChannel, lastTo, lastAccountId, lastThreadId, origin, and identifying session key metadata.
  • Archive or delete heavy transcript/artifact references once the protected retention policy applies.
  • Expose clear cleanup reporting so operators can see protected entries separately from normal entries.

Acceptance Criteria

  • Operators can bound old protected Slack channel/thread session growth with an explicit config.
  • session.maintenance.maxEntries reporting makes clear when protected entries are excluded.
  • Disk-budget cleanup can reclaim artifacts from old protected conversations when safe.
  • Slack routing for future messages/replies still works after old protected sessions are trimmed.
  • Cleanup output distinguishes normal pruned entries, capped entries, protected entries skipped, protected entries trimmed/archived, and artifacts reclaimed.

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…

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 Protected external conversation sessions can bypass maintenance caps and keep large artifacts alive