openclaw - ✅(Solved) Fix Session idle/daily reset never fires: updateLastRoute() bumps updatedAt before freshness check [5 pull requests, 3 comments, 4 participants]

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…
GitHub stats
openclaw/openclaw#49515Fetched 2026-04-08 00:54:25
View on GitHub
Comments
3
Participants
4
Timeline
12
Reactions
0
Author
Timeline (top)
cross-referenced ×5commented ×3referenced ×3subscribed ×1

Root Cause

recordInboundSession() calls updateLastRoute() which writes:

updatedAt: Math.max(existing?.updatedAt ?? 0, now)  // now = Date.now()

This uses mergeSessionEntry (not preserve-activity) and writes to sessions.json BEFORE initSessionState() reads it.

Sequence on every inbound message:

  1. Discord message arrives
  2. recordInboundSession()updateLastRoute() bumps updatedAt to Date.now()
  3. sessions.json now shows fresh updatedAt
  4. getReplyFromConfig()initSessionState()loadSessionStore(skipCache: true)
  5. evaluateSessionFreshness() sees updatedAt = Date.now(), returns fresh: true
  6. Session reuses indefinitely

Location: Line ~33953 in dist/auth-profiles-DRjqKE3G.js (updateLastRoute function)

Fix Action

Workaround

Manual /reset in affected channels. No config-based workaround exists since the bug is in the inbound pipeline before config-based freshness evaluation.

PR fix notes

PR #49539: fix(sessions): preserve updatedAt in updateLastRoute to allow idle/daily resets

Description (problem / solution / changelog)

Fixes #49515.

What was happening

updateLastRoute is called on every inbound message via recordInboundSession. It builds a basePatch that includes updatedAt: Math.max(existing?.updatedAt ?? 0, now), then called mergeSessionEntry to persist. mergeSessionEntry applies another Math.max(..., Date.now()) pass, so the session updatedAt was bumped to wall-clock time on every received message.

This meant evaluateSessionFreshness always saw a fresh session — idleMinutes and atHour resets never fired. Issue #49515 documents a concrete case: a session ran 2.5 days, 1084 entries, through 16 idle gaps with zero resets.

The fix

recordSessionMetaFromInbound was already patched in #32379 to use mergeSessionEntryPreserveActivity for the same reason — inbound metadata should not count as session activity. updateLastRoute was a missed call site with the same bug.

Changed one call in updateLastRoute:

- const next = mergeSessionEntry(
+ const next = mergeSessionEntryPreserveActivity(
    existing,
    metaPatch ? { ...basePatch, ...metaPatch } : basePatch,
  );

mergeSessionEntryPreserveActivity preserves existing.updatedAt for existing sessions (the preserve-activity policy in resolveMergedUpdatedAt). New sessions (no existing entry) still get a fresh timestamp via the normal fallback — correct behavior.

The basePatch.updatedAt field is left intact; it is dead computation for existing sessions under the preserve-activity policy, but removing it would widen scope beyond this fix.

Test

Added store.update-last-route.test.ts with two cases: existing session keeps its original updatedAt, new session gets a fresh timestamp.

Changed files

  • src/config/sessions/store.ts (modified, +1/-1)
  • src/config/sessions/store.update-last-route.test.ts (added, +75/-0)

PR #49588: fix(sessions): updateLastRoute must not bump updatedAt (#49515)

Description (problem / solution / changelog)

Fixes #49515

Problem

updateLastRoute() used mergeSessionEntry which bumps updatedAt to Date.now() on every inbound message. This prevented session idle and daily reset from ever firing, since evaluateSessionFreshness() always saw a fresh updatedAt.

The fix from #32379 patched recordSessionMetaFromInbound to use mergeSessionEntryPreserveActivity, but missed updateLastRoute() in the same inbound pipeline.

Fix

  • Remove explicit updatedAt from updateLastRoute basePatch (routing should not write activity timestamps)
  • Switch from mergeSessionEntry to mergeSessionEntryPreserveActivity (belt-and-suspenders: resolveMergedUpdatedAt has a Date.now() fallback that would still bump activity without the preserve-activity policy)
  • Add regression test verifying updatedAt is preserved across route updates
  • Update existing test assertion to match corrected behavior

Context

The comment at line 860 of store.ts already documents the principle:

Inbound metadata updates must not refresh activity timestamps; idle reset evaluation relies on updatedAt from actual session turns.

This PR applies the same principle to updateLastRoute(), completing the fix started in #32379.

Testing

  • New test: updateLastRoute does not bump updatedAt on existing sessions (#49515)
  • Updated existing test: updateLastRoute persists channel and target — assertion changed from toBeGreaterThanOrEqual(123) to toBe(123)

Changed files

  • src/config/sessions.test.ts (modified, +32/-1)
  • src/config/sessions/store.ts (modified, +3/-2)

PR #49625: fix(sessions): updateLastRoute must not bump updatedAt

Description (problem / solution / changelog)

Summary

  • Removes updatedAt from route-only updates so idle/daily reset timers are not inadvertently refreshed
  • Switches to mergeSessionEntryPreserveActivity which already exists for this purpose
  • Removes unused now variable left over after the updatedAt removal

Fixes #49515

Test plan

  • Existing test assertion tightened: toBeGreaterThanOrEqual(123) -> toBe(123)
  • New dedicated test: updateLastRoute does not bump updatedAt on existing sessions (#49515)
  • All 73 session tests pass (11 test files)
  • CI green

🤖 Generated with Claude Code

Changed files

  • src/config/sessions.test.ts (modified, +32/-1)
  • src/config/sessions/store.ts (modified, +3/-3)

PR #49628: fix(sessions): updateLastRoute must not bump updatedAt

Description (problem / solution / changelog)

Summary

  • updateLastRoute() was calling mergeSessionEntry which bumps updatedAt to Date.now() on every inbound message
  • This made every session always appear "fresh", so initSessionState() idle/daily reset checks never triggered
  • Changed to mergeSessionEntryPreserveActivity so updatedAt reflects the last actual agent turn, not the last routing metadata write

Root cause

In src/config/sessions/store.ts, the updateLastRoute function used mergeSessionEntry (line 942) which calls resolveMergedUpdatedAt with the default "touch-activity" policy, always setting updatedAt = Math.max(existing.updatedAt, Date.now()). Since recordInboundSession calls updateLastRoute on every inbound message, updatedAt was perpetually bumped to now, and the freshness check in initSessionState never saw a stale session.

Fix

Replaced mergeSessionEntry with mergeSessionEntryPreserveActivity in updateLastRoute. The "preserve-activity" policy returns existing.updatedAt instead of bumping it, matching the pattern already used in recordSessionMetaFromInbound (same file, line 861).

Test plan

  • Existing tests pass: store.session-key-normalization.test.ts (4/4), session.test.ts (4/4), sessions.test.ts (19/19), config/sessions.test.ts (37/37)
  • Manual verification: confirm idle reset fires after configured timeout with active inbound messages

Fixes #49515

🤖 Generated with Claude Code

Changed files

  • src/config/sessions.test.ts (modified, +2/-1)
  • src/config/sessions/store.ts (modified, +7/-6)

PR #49852: fix(agents): respect provider config for MiniMax vision model selection

Description (problem / solution / changelog)

Problem

The image tool hard-codes MiniMax-VL-01 as the preferred vision model for MiniMax users, completely ignoring resolveProviderVisionModelFromConfig. When MiniMax-VL-01 appears in auto-discovered models.json but is not actually callable by the API, the image tool fails with an Unknown model error -- even when the user has a working vision model configured.

Root cause

In resolveImageModelConfigForTool (image-tool.ts), the MiniMax branch unconditionally assigns the hard-coded model:

if (isMinimaxVlmProvider(primary.provider) && providerOk) {
  preferred = \`\${primary.provider}/MiniMax-VL-01\`;
}

The providerVisionFromConfig result (already computed above from the user's actual provider config) is only used in the else if branch for non-MiniMax providers.

Fix

Use the provider-configured vision model when available, falling back to VL-01 only when no config entry exists:

preferred = providerVisionFromConfig ?? \`\${primary.provider}/MiniMax-VL-01\`;

This is safe because resolveProviderVisionModelFromConfig already preferentially selects MiniMax-VL-01 when it IS present in the config with image input capability. So the behavior is unchanged when VL-01 is configured and callable. When it is not configured (returns null), the hard-coded fallback preserves backward compatibility.

Scope

1 file changed, 2 insertions, 2 deletions.

Fixes #49838

Changed files

  • src/agents/tools/image-tool.ts (modified, +2/-2)
  • src/config/sessions/store.ts (modified, +1/-1)
  • src/config/sessions/store.update-last-route.test.ts (added, +75/-0)

Code Example

updatedAt: Math.max(existing?.updatedAt ?? 0, now)  // now = Date.now()
RAW_BUFFERClick to expand / collapse

Bug Description

Session idle and daily resets never fire because updateLastRoute() in recordInboundSession() bumps updatedAt to Date.now() before initSessionState() evaluates session freshness.

Environment

  • OpenClaw version: 2026.3.13 (61d171a)
  • Platform: macOS (Darwin arm64), Node v25.6.1
  • Channel: Discord (multi-agent setup, 10 agents)
  • Config: session.reset = { mode: "daily", atHour: 4, idleMinutes: 20 }

Steps to Reproduce

  1. Configure session reset with idleMinutes: 20 and mode: "daily", atHour: 4
  2. Send a message in a Discord channel session
  3. Wait >20 minutes (or past the daily reset hour)
  4. Send another message
  5. Session is NOT reset; same sessionId, transcript keeps growing

Evidence

  • F.R.I.D.A.Y. agent: Session ran 2.5 days (created 2026-03-15T20:50 UTC), 1084 entries, 5.2MB, 367K tokens, through 16 idle gaps >20 min (longest: 17.6 hours)
  • JARVIS agent: 268K tokens, 14.1-hour and 9.1-hour gaps without reset
  • All agents affected (systemic)

Root Cause

recordInboundSession() calls updateLastRoute() which writes:

updatedAt: Math.max(existing?.updatedAt ?? 0, now)  // now = Date.now()

This uses mergeSessionEntry (not preserve-activity) and writes to sessions.json BEFORE initSessionState() reads it.

Sequence on every inbound message:

  1. Discord message arrives
  2. recordInboundSession()updateLastRoute() bumps updatedAt to Date.now()
  3. sessions.json now shows fresh updatedAt
  4. getReplyFromConfig()initSessionState()loadSessionStore(skipCache: true)
  5. evaluateSessionFreshness() sees updatedAt = Date.now(), returns fresh: true
  6. Session reuses indefinitely

Location: Line ~33953 in dist/auth-profiles-DRjqKE3G.js (updateLastRoute function)

Relationship to #32379

The idle reset fix in #32379 (v2026.3.2) patched recordSessionMetaFromInbound to use preserve-activity, but missed updateLastRoute() in the same recordInboundSession() function. The bug is an incomplete fix.

Suggested Fix

Make updateLastRoute() use mergeSessionEntryPreserveActivity instead of mergeSessionEntry, or remove the updatedAt field from its patch object. updatedAt should only be bumped by the actual agent run completion in updateSessionStoreAfterAgentRun().

Workaround

Manual /reset in affected channels. No config-based workaround exists since the bug is in the inbound pipeline before config-based freshness evaluation.

extent analysis

Fix Plan

To resolve the issue, we need to modify the updateLastRoute() function to prevent it from updating the updatedAt field. We can achieve this by removing the updatedAt field from the patch object.

Step-by-Step Solution

  • Locate the updateLastRoute() function in the dist/auth-profiles-DRjqKE3G.js file (around line 33953).
  • Update the updateLastRoute() function to remove the updatedAt field from the patch object:
// Before
const patch = {
  updatedAt: Math.max(existing?.updatedAt ?? 0, now),
  // ... other fields
};

// After
const patch = {
  // Remove the updatedAt field
  // updatedAt: Math.max(existing?.updatedAt ?? 0, now),
  // ... other fields
};

Alternatively, you can use mergeSessionEntryPreserveActivity instead of mergeSessionEntry to preserve the activity without updating the updatedAt field.

Verification

After applying the fix, verify that the session is reset correctly by:

  • Waiting for the idle timeout (20 minutes) or the daily reset hour (4 AM).
  • Sending a new message in the Discord channel.
  • Checking that the session is reset and a new session ID is generated.

Extra Tips

  • Make sure to test the fix thoroughly to ensure that it resolves the issue without introducing any new problems.
  • Consider implementing additional logging or monitoring to detect similar issues in the future.
  • Review the related issue #32379 and ensure that the fix is consistent with the changes made in that issue.

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 - ✅(Solved) Fix Session idle/daily reset never fires: updateLastRoute() bumps updatedAt before freshness check [5 pull requests, 3 comments, 4 participants]