Problem
OpenCode can stop a session after a provider-side abort even when the user did not cancel the run.
In the incident we investigated, an assistant message sat for ~16 minutes, produced zero tokens / zero parts, and then completed with:
MessageAbortedError
message: \"The operation was aborted.\"
The last tool call had already completed successfully. The next model call never returned any content, then the session stopped instead of retrying.
Actual behavior
When SessionProcessor catches a DOMException / AbortError, MessageV2.fromError() converts it into MessageAbortedError.
SessionRetry.retryable() does not treat that error as retryable, so the processor:
- sets
assistantMessage.error
- publishes the error
- returns
\"stop\"
This makes the session appear stuck/cancelled even though the user did not explicitly cancel it.
Expected behavior
If the session's own abort signal was not triggered, an unexpected AbortError from the model/provider layer should be treated as transient and retried with the normal retry/backoff flow.
A user-initiated cancel should still stop immediately and remain non-retryable.
Root cause
Current logic treats all AbortErrors as equivalent:
- user cancellation via
SessionPrompt.cancel()
- provider/client/network aborts while waiting for streamed output
Those cases need to be separated.
Proposed fix
- detect
MessageAbortedError in SessionProcessor
- if
input.abort.aborted === false, classify it as retryable and use the existing retry/backoff status flow
- if
input.abort.aborted === true, keep the current cancel behavior
- add tests covering both cases
Acceptance criteria
- unexpected
AbortErrors retry automatically instead of stopping the session
- explicit session cancellation still stops immediately
- retry status is surfaced through existing
SessionStatus retry flow
- tests cover both retryable and non-retryable abort cases
Problem
OpenCode can stop a session after a provider-side abort even when the user did not cancel the run.
In the incident we investigated, an assistant message sat for ~16 minutes, produced zero tokens / zero parts, and then completed with:
MessageAbortedErrormessage: \"The operation was aborted.\"The last tool call had already completed successfully. The next model call never returned any content, then the session stopped instead of retrying.
Actual behavior
When
SessionProcessorcatches aDOMException/AbortError,MessageV2.fromError()converts it intoMessageAbortedError.SessionRetry.retryable()does not treat that error as retryable, so the processor:assistantMessage.error\"stop\"This makes the session appear stuck/cancelled even though the user did not explicitly cancel it.
Expected behavior
If the session's own abort signal was not triggered, an unexpected
AbortErrorfrom the model/provider layer should be treated as transient and retried with the normal retry/backoff flow.A user-initiated cancel should still stop immediately and remain non-retryable.
Root cause
Current logic treats all
AbortErrors as equivalent:SessionPrompt.cancel()Those cases need to be separated.
Proposed fix
MessageAbortedErrorinSessionProcessorinput.abort.aborted === false, classify it as retryable and use the existing retry/backoff status flowinput.abort.aborted === true, keep the current cancel behaviorAcceptance criteria
AbortErrors retry automatically instead of stopping the sessionSessionStatusretry flow