Repository navigation
Conversation
campoy
force-pushed
the
fix/strands-typescript-model-cancel-signal-lost
branch
from
October 30, 2026 23:36
f6c47a9 to
7038d00
Compare
campoy
marked this pull request as ready for review
October 31, 2026 01:35
Strands passes the agent's cancelSignal, an AbortSignal, in the model's stream options. The model activity got it as a plain object, so OpenAIModel, which hands it to the OpenAI client, failed every call with "signal.addEventListener is not a function" before sending a request, from @strands-agents/sdk 1.15.0 on. The model activities now give the model the activity's own cancellation signal instead. Cancelling the activity then aborts the model call, as cancelling the agent does natively.
campoy
force-pushed
the
fix/strands-typescript-model-cancel-signal-lost
branch
from
October 31, 2026 01:52
3952aed to
ecc7667
Compare
Other tests on main use ModelActivity. Rebasing the commit that removed this PR's own copy of the import also removed main's.
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What was changed
The model Activities give the model the Activity's own cancellation signal
(
Context.current().cancellationSignal) ascancelSignal, in place of the agent's.Strands puts the agent's
cancelSignal, anAbortSignal, in the model's stream options. It reachedthe Activity as a plain object (
{}). From@strands-agents/sdk1.15.0,OpenAIModelhands it to theOpenAI client, which calls
signal.addEventListener, so everyOpenAIModelcall through the pluginfailed before its request, Responses and Chat Completions alike. That includes the plugin's locked
1.19.0 and the latest 1.22.0.
With the Activity's signal, the request is sent as it is natively. When the model Activity has a
heartbeatTimeout, cancelling it aborts the signal; a model that passescancelSignalto its client(in Strands 1.19.0,
OpenAIModelandBedrockModel) then stops the provider call, as cancelling theagent does natively.
AnthropicModel,GoogleModelandVercelModelignorecancelSignal, so theirprovider call keeps running and the Activity can still complete with its result. A remote Activity
learns of cancellation only through heartbeats, so without a
heartbeatTimeoutthe model call runs tocompletion. The README's new Cancellation section documents this, and how
TemporalAgentcancellationtypes behave.
Model clients abort with their own errors (OpenAI's
APIUserAbortErroris namedError), whichTemporal would record as an Activity failure. When the model throws after the Activity's signal
aborted, the Activity throws the signal's
CancelledFailureinstead, so the Activity ends ascancelled. One helper in
model-activity.tsdoes this forinvokeModelandinvokeModelStreaming. Workflow code and the Activity input do not change.Why?
See #296.
Compatibility
No version gate is needed, and nothing that works today breaks:
object without ever aborting (Bedrock's HTTP handler sets
onaborton it) now get a signal thataborts when the model Activity is cancelled with a
heartbeatTimeoutset. Natively, cancelling theagent aborts their call the same way.
OpenAIModelcalls; during a rollout they are retrieduntil an upgraded Worker takes them.
Checklist
Closes [Bug] strands-agents (TypeScript): OpenAIModel calls fail in the model Activity on Strands 1.15.0 and later #296
How was this tested:
invokeModel gives the model Message instances and an AbortSignal(the existing wire-datatest, extended) checks that the model gets an
AbortSignal. It fails onmain(
expected cancelSignal to be an AbortSignal).cancelling the workflow aborts the model call in invokeModel and cancels the workflow, andthe same for
invokeModelStreaming, runTemporalModel.streamon a real Worker withheartbeatTimeoutandWAIT_CANCELLATION_COMPLETED, cancel the Workflow mid-call, and use amodel that throws an
ErrornamedErroronce its signal aborts, as OpenAI's client does. Theycheck that the model's signal aborted and that the history has
ActivityTaskCanceled, noActivityTaskFailed, andWorkflowExecutionCanceled. Without theCancelledFailurerethrowboth fail with
expected a CancelledFailure, got ActivityFailure: Activity task failed.cancelling a TemporalAgent workflow with TRY_CANCEL aborts the model call and cancels the workflow, and the same withWAIT_CANCELLATION_COMPLETED, runagent.invoke()on a real Workerwith
heartbeatTimeoutand cancel the Workflow mid-call. They check that the model's signalaborts while the Worker runs and that the history has
ActivityTaskCancelRequested,WorkflowExecutionCanceledand noWorkflowTaskFailed(plusActivityTaskCanceledwithWAIT_CANCELLATION_COMPLETED). WithTemporalAgent'sModelErrorunwrap disabled, both time outwith repeated
WorkflowTaskFailed.cancelling a TemporalAgent workflow with TRY_CANCEL while the model ignores its signal, and thesame with
WAIT_CANCELLATION_COMPLETED, use a model that keeps going after its signal aborts andreturns
late result. WithTRY_CANCELthe run is cancelled. WithWAIT_CANCELLATION_COMPLETEDthe run completes with
late result, and the history hasActivityTaskCompleted,WorkflowExecutionCompletedand noWorkflowExecutionCanceled.OpenAIModeland a recordingfetch, Responses and ChatCompletions: on
mainit fails with@strands-agents/sdk1.15.0, 1.19.0 and 1.22.0 and passeswith 1.14.0; with the signal change it passes on all four. Also with
@temporalio/*1.25.0 andStrands 1.22.0. (This ran before the
CancelledFailurerethrow was added.)cancelSignalwas taken from the locked Strands 1.19.0 sources: onlyOpenAIModelandBedrockModelpass it to their clients.make sync,make lint,make test(86 tests),make buildandcheck_conventions.pypass. The Node 22, macOS and Windows cells did not run locally.Any docs updates needed?
The README gains a Cancellation section with the
heartbeatTimeoutrequirement, the dependence onthe model honoring
cancelSignal, and howTemporalAgent'sTRY_CANCELandWAIT_CANCELLATION_COMPLETEDcancellation types differ, including that withWAIT_CANCELLATION_COMPLETEDa model Activity that completes after cancellation lets the runcomplete normally. The Models section's note from fix(strands): run stateful models with their server-side state (breaking) #285, which said
OpenAIModelcalls work onlybefore
@strands-agents/sdk1.15.0, is rewritten: such calls work with current releases, and onlya Worker without this change still fails them during a rollout.
Not covered
agent.cancel()in Workflow code still does not cancel the model Activity; cancel it throughTemporal (a cancellation scope or a Workflow cancel).
cancelSignal(in Strands 1.19.0,AnthropicModel,GoogleModelandVercelModel) are not aborted, and their Activity can complete with the model's result aftercancellation. With
WAIT_CANCELLATION_COMPLETED, the agent then continues with that result and therun can complete instead of being cancelled.