-
-
Notifications
You must be signed in to change notification settings - Fork 80.7k
Discord second message fails: false stale-snapshot from skillsSnapshot hydration mismatch #96698
Copy link
Copy link
Closed
Labels
P1High-priority user-facing bug, regression, or broken workflow.High-priority user-facing bug, regression, or broken workflow.clawsweeper:fix-shape-clearClawSweeper found a clear likely implementation shape for this issue.ClawSweeper found a clear likely implementation shape for this issue.clawsweeper:linked-pr-openClawSweeper found an open linked pull request for this issue.ClawSweeper found an open linked pull request for this issue.clawsweeper:no-new-fix-prClawSweeper does not recommend queueing a new automated fix PR for this issue.ClawSweeper does not recommend queueing a new automated fix PR for this issue.clawsweeper:queueable-fixClawSweeper marked this issue as an existing queue_fix_pr work candidate.ClawSweeper marked this issue as an existing queue_fix_pr work candidate.clawsweeper:source-reproClawSweeper found a high-confidence source-level issue reproduction.ClawSweeper found a high-confidence source-level issue reproduction.impact:message-lossChannel message delivery can be lost, duplicated, or misrouted.Channel message delivery can be lost, duplicated, or misrouted.impact:session-stateSession, memory, transcript, context, or agent state can drift or corrupt.Session, memory, transcript, context, or agent state can drift or corrupt.issue-rating: 🦞 diamond lobsterVery strong issue quality with high-confidence source-level or clear reproduction.Very strong issue quality with high-confidence source-level or clear reproduction.no-staleExclude from stale automationExclude from stale automation
Description
Metadata
Metadata
Assignees
Labels
P1High-priority user-facing bug, regression, or broken workflow.High-priority user-facing bug, regression, or broken workflow.clawsweeper:fix-shape-clearClawSweeper found a clear likely implementation shape for this issue.ClawSweeper found a clear likely implementation shape for this issue.clawsweeper:linked-pr-openClawSweeper found an open linked pull request for this issue.ClawSweeper found an open linked pull request for this issue.clawsweeper:no-new-fix-prClawSweeper does not recommend queueing a new automated fix PR for this issue.ClawSweeper does not recommend queueing a new automated fix PR for this issue.clawsweeper:queueable-fixClawSweeper marked this issue as an existing queue_fix_pr work candidate.ClawSweeper marked this issue as an existing queue_fix_pr work candidate.clawsweeper:source-reproClawSweeper found a high-confidence source-level issue reproduction.ClawSweeper found a high-confidence source-level issue reproduction.impact:message-lossChannel message delivery can be lost, duplicated, or misrouted.Channel message delivery can be lost, duplicated, or misrouted.impact:session-stateSession, memory, transcript, context, or agent state can drift or corrupt.Session, memory, transcript, context, or agent state can drift or corrupt.issue-rating: 🦞 diamond lobsterVery strong issue quality with high-confidence source-level or clear reproduction.Very strong issue quality with high-confidence source-level or clear reproduction.no-staleExclude from stale automationExclude from stale automation
Type
Fields
Priority
None yet
Summary
On Discord (and likely any channel using reply session initialization), the first inbound message succeeds but the second message always fails with:
Root cause is a false positive
stale-snapshotin optimistic concurrency:createReplySessionInitializationRevision()serializes the full session entry viaJSON.stringify, but snapshot load and commit compare different shapes ofskillsSnapshot.Environment
a96418c)main), minimal configRoot cause
loadReplySessionInitializationSnapshot()loads the store with default hydration, which persistsskillsSnapshotas{ skills: [...], promptRef: "..." }.Inside
commitReplySessionInitialization(), the writer's in-memorystoreis loaded with hydrated skill prompt blobs, so the same field becomes{ skills: [...], prompt: "<full text>" }.Revision comparison then fails even when
updatedAt/lastInteractionAtare identical. Diagnostic logs showed:fieldDiff.changedKeys: ['skillsSnapshot']firstDiffIndexaround 1036"skills"(expected) vs"prompt"(actual)After the first agent run writes a full
skillsSnapshot, the second inbound turn's init snapshot no longer matches the commit-time revision built from the hydrated writer cache → retry once → hard conflict.Suggested fix
Use the same persisted shape for revision on both paths:
loadReplySessionInitializationSnapshot:loadSessionStore(..., { hydrateSkillPromptRefs: false })commitReplySessionInitialization: when building the revision for comparison, re-read from disk withhydrateSkillPromptRefs: falseinstead of using the writer cache entry (which may be hydrated).Alternative: change revision to exclude or normalize
skillsSnapshot.promptvspromptRef.Secondary issue (optional hardening)
In
src/channels/session.ts,recordInboundSessionusedvoid metaTaskbeforeupdateLastRoute, allowing concurrent writes to the same session store. Changing toawait metaTaskavoids an additional race (not the primary cause of the every-second-message failure).Verified locally
With the two changes above, consecutive Discord messages succeed (no
stale-snapshot, model fetch 200 on second turn).