Race condition in session prompt state management #8269

Open
opened 2026-02-16 18:09:33 -05:00 by yindo · 1 comment
Owner

Originally created by @riftzen-bit on GitHub (Feb 1, 2026).

Originally assigned to: @rekram1-node on GitHub.

Summary

The session prompt module has a race condition in its state management that can cause unexpected behavior when multiple concurrent operations access the same session.

Location

packages/opencode/src/session/prompt.ts:239-264

Issue

The start() function has a check-then-act pattern without atomicity:

function start(sessionID: string) {
  const s = state()
  if (s[sessionID]) return  // CHECK
  const controller = new AbortController()
  s[sessionID] = {          // ACT - race condition window between check and act
    abort: controller,
    callbacks: [],
  }
  return controller.signal
}

Between the check at line 241 and the write at line 243, another concurrent call could:

  1. Also pass the check (sees no entry)
  2. Create its own entry
  3. Overwrite the first entry

Additionally, the loop() function at line 267-273 can push to callbacks array while the state is being modified.

Impact

  • Severity: Medium
  • Type: Race Condition
  • Effect:
    • Lost abort controllers
    • Callbacks registered to wrong session
    • Potential undefined access errors

Suggested Fix

Use the existing Lock utility for synchronization:

import { Lock } from "../util/lock"

const sessionLock = new Lock()

async function start(sessionID: string) {
  return await Lock.write(sessionID, async () => {
    const s = state()
    if (s[sessionID]) return undefined
    const controller = new AbortController()
    s[sessionID] = {
      abort: controller,
      callbacks: [],
    }
    return controller.signal
  })
}
Originally created by @riftzen-bit on GitHub (Feb 1, 2026). Originally assigned to: @rekram1-node on GitHub. ## Summary The session prompt module has a race condition in its state management that can cause unexpected behavior when multiple concurrent operations access the same session. ## Location `packages/opencode/src/session/prompt.ts:239-264` ## Issue The `start()` function has a check-then-act pattern without atomicity: ```typescript function start(sessionID: string) { const s = state() if (s[sessionID]) return // CHECK const controller = new AbortController() s[sessionID] = { // ACT - race condition window between check and act abort: controller, callbacks: [], } return controller.signal } ``` Between the check at line 241 and the write at line 243, another concurrent call could: 1. Also pass the check (sees no entry) 2. Create its own entry 3. Overwrite the first entry Additionally, the `loop()` function at line 267-273 can push to callbacks array while the state is being modified. ## Impact - **Severity**: Medium - **Type**: Race Condition - **Effect**: - Lost abort controllers - Callbacks registered to wrong session - Potential undefined access errors ## Suggested Fix Use the existing Lock utility for synchronization: ```typescript import { Lock } from "../util/lock" const sessionLock = new Lock() async function start(sessionID: string) { return await Lock.write(sessionID, async () => { const s = state() if (s[sessionID]) return undefined const controller = new AbortController() s[sessionID] = { abort: controller, callbacks: [], } return controller.signal }) } ```
Author
Owner

@github-actions[bot] commented on GitHub (Feb 1, 2026):

This issue might be a duplicate of existing issues. Please check:

  • #5517: OpenCode API Race Condition in Concurrent Session Deletion
  • #7882: bug(webui): archived session "flashes back" due to SSE race condition
  • #10578: New session UI disappears when stored session list response is late
  • #3856: race condition when applying changes immediatly after each other on same file
  • #4251: Concurrent sessions working on different repos interfer each other
  • #11289: fix: add missing await in prompt_async handler - CRITICAL: Empty response bug
  • #6653: Interrupting long session then resuming results in Internal Server Error
  • #11512: Agent response not visible to user despite responding correctly
  • #10913: fix: multiple memory leaks in long-running sessions

Feel free to ignore if none of these address your specific case.

@github-actions[bot] commented on GitHub (Feb 1, 2026): This issue might be a duplicate of existing issues. Please check: - #5517: OpenCode API Race Condition in Concurrent Session Deletion - #7882: bug(webui): archived session "flashes back" due to SSE race condition - #10578: New session UI disappears when stored session list response is late - #3856: race condition when applying changes immediatly after each other on same file - #4251: Concurrent sessions working on different repos interfer each other - #11289: fix: add missing await in prompt_async handler - CRITICAL: Empty response bug - #6653: Interrupting long session then resuming results in Internal Server Error - #11512: Agent response not visible to user despite responding correctly - #10913: fix: multiple memory leaks in long-running sessions Feel free to ignore if none of these address your specific case.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: anomalyco/opencode#8269