Skip to content

fix: prevent unbounded narrative session growth by stabilizing session key (#68354)#68364

Closed
jasonmakr wants to merge 1 commit into
openclaw:mainfrom
jasonmakr:fix/dreaming-session-key-reuse
Closed

fix: prevent unbounded narrative session growth by stabilizing session key (#68354)#68364
jasonmakr wants to merge 1 commit into
openclaw:mainfrom
jasonmakr:fix/dreaming-session-key-reuse

Conversation

@jasonmakr

Copy link
Copy Markdown

Problem

buildNarrativeSessionKey includes a millisecond timestamp (nowMs from Date.now()) in the session key, making it unique per dream run:

return `dreaming-narrative-${params.phase}-${workspaceHash}-${params.nowMs}`;

When deleteSession fails in the finally block, these uniquely-keyed sessions accumulate indefinitely. As reported in #68354: 251 orphan sessions in 4 days, consuming ~4.8M tokens.

Fix

1. Stabilize session key (remove nowMs)

return `dreaming-narrative-${params.phase}-${workspaceHash}`;

Now each workspace+phase combination reuses the same session key. If deleteSession fails, the next run reuses the existing session instead of creating a new orphan. Maximum accumulation: 2-3 sessions (one per phase) instead of unbounded.

2. Keep nowMs in idempotency key

idempotencyKey: `${params.sessionKey}-${params.nowMs}`,

Prevents deduplication across dream runs — each run is still treated as unique by the subagent runtime.

3. Pre-run defensive cleanup

Added a deleteSession call before subagent.run() to clear any leftover session from a previous failed run. This prevents stale conversation context from polluting the new narrative.

// Defensive cleanup: delete any leftover session from a previous failed run
try {
  await params.subagent.deleteSession({ sessionKey });
} catch {
  // Ignore — session may not exist yet.
}

Trade-offs considered

  • Stale context: If deleteSession fails in finally AND the pre-run cleanup also fails, the next run could see stale messages. This is a minor cosmetic risk (dream diary entries slightly influenced by previous context) vs. the current unbounded resource leak.
  • Concurrent runs: Dream runs are cron-scheduled, so concurrent same-workspace/phase runs are unlikely. The stable key makes them share a session if they do overlap, which is safer than creating parallel orphans.

Tests updated

  • Updated deleteSession call count expectations (now 2 per run: pre-run cleanup + finally)
  • Updated workspace isolation test to verify delete keys per workspace
  • Updated idempotency key expectations

Closes #68354

…n key (openclaw#68354)

The buildNarrativeSessionKey function included a millisecond timestamp
(nowMs) in the session key, creating a unique session for every dream
run. When session deletion fails in the finally block, these sessions
accumulate indefinitely (observed: 251 orphan sessions in 4 days).

Changes:
- Remove nowMs from session key → stable key per workspace+phase
- Keep nowMs in idempotencyKey to prevent run deduplication
- Add pre-run deleteSession call to clear stale sessions from previous
  failed runs, preventing conversation context pollution
- Update tests to reflect new deleteSession call count (2 per run:
  pre-run cleanup + finally cleanup)
@greptile-apps

greptile-apps Bot commented Apr 18, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR fixes the root cause of unbounded narrative session accumulation (#68354): the session key previously embedded nowMs, guaranteeing a new orphan session on every failed cleanup. The fix stabilizes the key to dreaming-narrative-${phase}-${workspaceHash} (bounding accumulation to one session per workspace+phase) while moving nowMs into the idempotency key to preserve per-run deduplication semantics. A pre-run deleteSession adds defense-in-depth by clearing any leftover session before each new run. Tests are correctly updated throughout.

Confidence Score: 5/5

Safe to merge — the fix correctly bounds session accumulation with no regressions introduced.

All remaining findings are P2 style suggestions. The core logic (stable session key, idempotency key carrying nowMs, pre-run cleanup) is correct. Tests are comprehensive and accurately reflect the 2-deleteSession-per-run invariant. The acknowledged trade-off (stale context if both cleanup calls fail) is minor and well-reasoned.

No files require special attention.

Prompt To Fix All With AI
This is a comment left during a code review.
Path: extensions/memory-core/src/dreaming-narrative.ts
Line: 860

Comment:
**Catch comment understates swallowed errors**

The comment says `// Ignore — session may not exist yet.` but the catch block also silently swallows real failures (network errors, auth errors, etc.). Since this is intentionally best-effort, the comment would be more accurate as:

```suggestion
    // Ignore — session may not exist yet, or cleanup may fail transiently.
```

Minor, but future readers chasing unexpected state may be misled by the narrower framing.

How can I resolve this? If you propose a fix, please make it concise.

Reviews (1): Last reviewed commit: "fix: prevent unbounded narrative session..." | Re-trigger Greptile

try {
await params.subagent.deleteSession({ sessionKey });
} catch {
// Ignore — session may not exist yet.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 Catch comment understates swallowed errors

The comment says // Ignore — session may not exist yet. but the catch block also silently swallows real failures (network errors, auth errors, etc.). Since this is intentionally best-effort, the comment would be more accurate as:

Suggested change
// Ignore — session may not exist yet.
// Ignore — session may not exist yet, or cleanup may fail transiently.

Minor, but future readers chasing unexpected state may be misled by the narrower framing.

Prompt To Fix With AI
This is a comment left during a code review.
Path: extensions/memory-core/src/dreaming-narrative.ts
Line: 860

Comment:
**Catch comment understates swallowed errors**

The comment says `// Ignore — session may not exist yet.` but the catch block also silently swallows real failures (network errors, auth errors, etc.). Since this is intentionally best-effort, the comment would be more accurate as:

```suggestion
    // Ignore — session may not exist yet, or cleanup may fail transiently.
```

Minor, but future readers chasing unexpected state may be misled by the narrower framing.

How can I resolve this? If you propose a fix, please make it concise.

@prtags

prtags Bot commented Apr 23, 2026

Copy link
Copy Markdown

Related work from PRtags group honest-kangaroo-d8gt

Title: Open PR candidate: memory-core dreaming-narrative session leak cleanup

Number Title
#67023 fix(memory-core): prevent dreaming-narrative session leaks (#66358)
#68364* fix: prevent unbounded narrative session growth by stabilizing session key (#68354)
#70464 fix(memory-core): allow bounded dreaming session cleanup

* This PR

@clawsweeper

clawsweeper Bot commented Apr 26, 2026

Copy link
Copy Markdown
Contributor

Closing this as duplicate or superseded after Codex automated review.

PR #68364 is a narrow stable-session-key patch that remains absent from current main, but it is superseded by open PR #70464, which carries the same memory-core dreaming narrative key/idempotency/pre-cleanup fix and adds the missing scoped gateway permission needed for cleanup to work.

Best possible solution:

Close #68364 as superseded and keep review focused on #70464 or its successor, because that path combines the stable workspace+phase narrative session key, per-run idempotency key, pre-run cleanup, and the narrow gateway permission needed for memory-core to delete its own transient dreaming sessions.

What I checked:

So I’m closing this here and keeping the remaining discussion on the canonical linked item.

Codex Review notes: model gpt-5.5, reasoning high; reviewed against d54d2d6b9b8a.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Dreaming narrative sessions never reuse — session key includes timestamp causing unbounded growth

1 participant