Skip to content

Commit 4a8fca8

Browse files
altimate-harness-bot[bot]saravmajestic
andauthored
fix: [AI-6771] cancel() race condition and idle state on normal loop exit (#845)
* fix: [AI-6771] cancel() race condition and idle state on normal loop exit Fix B1: Remove premature SessionStatus.set(idle) from cancel() main path. On abort, the processor catch block publishes session.error then sets idle, preserving correct event ordering. The !match fallback still sets idle directly since no processor is running in that case. Fix B2: Add SessionStatus.set(idle) after the while loop exits normally so callers waiting on session.status:idle receive the event on clean completion. * feat: [AI-6771] add telemetry for streaming error scenarios Add Telemetry.track({ type: "error", context: "streaming" }) in the non-retry, non-overflow branch of the processor.ts streaming catch block. Covers: - MessageAbortedError (Stop button / dispose) - UnknownError (SSE chunk timeout after retry exhaustion) - APIError (provider failures after retry exhaustion) - AuthError and any other unhandled streaming error Event fields: session_id, error_name, error_message, context * fix: reword cancel() comment to avoid false-positive in SessionStatus guard test --------- Co-authored-by: saravmajestic <saravmajestic@altimate.ai> Co-authored-by: altimate-harness-bot[bot] <274995457+altimate-harness-bot[bot]@users.noreply.github.com>
1 parent bc50b59 commit 4a8fca8

2 files changed

Lines changed: 21 additions & 2 deletions

File tree

packages/opencode/src/session/processor.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -533,6 +533,18 @@ export namespace SessionProcessor {
533533
sessionID: input.assistantMessage.sessionID,
534534
error: input.assistantMessage.error,
535535
})
536+
// altimate_change start — telemetry for unhandled streaming errors (non-retry, non-overflow)
537+
// Covers: MessageAbortedError (Stop/dispose), UnknownError (SSE chunk timeout),
538+
// APIError (provider failures after retry exhaustion), AuthError, and any other streaming error.
539+
Telemetry.track({
540+
type: "error",
541+
timestamp: Date.now(),
542+
session_id: input.assistantMessage.sessionID,
543+
error_name: error.name,
544+
error_message: (error.data as any)?.message ?? String((e as any)?.message ?? ""),
545+
context: "streaming",
546+
})
547+
// altimate_change end
536548
// altimate_change start — SessionStatus.set became async; await so idle state flushes before exit
537549
await SessionStatus.set(input.sessionID, { type: "idle" })
538550
// altimate_change end

packages/opencode/src/session/prompt.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -303,13 +303,15 @@ export namespace SessionPrompt {
303303
const s = state()
304304
const match = s[sessionID]
305305
if (!match) {
306+
// Session already ended or was never started — set idle directly since no processor will do it
306307
await SessionStatus.set(sessionID, { type: "idle" })
307308
return
308309
}
309310
match.abort.abort()
310311
delete s[sessionID]
311-
await SessionStatus.set(sessionID, { type: "idle" })
312-
return
312+
// Do NOT set idle status here — on abort the processor's catch block
313+
// publishes session.error THEN sets idle, preserving correct event ordering.
314+
// On normal completion, loop() sets idle after the while loop exits (see below).
313315
}
314316
// altimate_change end
315317

@@ -1110,6 +1112,11 @@ export namespace SessionPrompt {
11101112
}
11111113
continue
11121114
}
1115+
// altimate_change start — set idle on normal loop exit; abort path is handled by processor catch block
1116+
if (!abort.aborted) {
1117+
await SessionStatus.set(sessionID, { type: "idle" })
1118+
}
1119+
// altimate_change end
11131120
SessionCompaction.prune({ sessionID })
11141121
// altimate_change start — session end telemetry
11151122
const outcome = abort.aborted

0 commit comments

Comments
 (0)