Hello!
I have a workflow that sometimes can be cancelled externally from the Temporal UI using the Request Cancel button and I’m unable to do it without receiving a Temporal error
I copied the same lines of code that we have in the TypeScript SDK tutorial here: Interrupt a Workflow - TypeScript SDK | Temporal Platform Documentation
It works to detect an external cancel, but doesn’t works when I try to execute an activity during cancelation.
Example:
import * as wf from "@temporalio/workflow";
import { CancellationScope, isCancellation, proxyActivities } from "@temporalio/workflow";
import type { actionActivities } from "@temporal/activities";
const {
recordAction,
completeAction,
rejectAction,
cancelAction,
expireAction
} = proxyActivities<typeof actionActivities>({
taskQueue: "action",
startToCloseTimeout: "10 seconds",
});
export async function myActionWorkflow(workflowId: string, args: ActionWorkflowArgs) {
// Example code
await recordAction(args);
// This one works perfect
wf.setHandler(cancelSignal, async (input: CancelArgs) => {
await cancelAction(args, input);
});
wf.setHandler(rejectSignal, async (input: RejectArgs) => {
await rejectAction(args, input);
});
try {
const timer = createTimer(86000);
const timerExpiredPromise = timer.then(async () => {
await expireAction(args);
});
// Just an example to don't show unnecessary code
await wf.sleep("3 day");
} catch (error) {
if (isCancellation(error)) {
wf.log.info("Workflow cancelled externally"); // I see this log
await CancellationScope.nonCancellable(() =>
cancelAction(args, {}),
);
wf.log.info("Cleanup: cancelAction completed"); // I don't see this log
}
}
}
The code in cancelAction is just a SQL insert in Drizzle.
export const cancelAction = async (...) => {
const [inserted] = await db
.insert(traces)
.values({
id: uuid(),
actionId: actionId,
status: "CANCEL"
})
.returning();
if (!inserted) {
throw new Error("Failed to create trace");
}
return { traceId: inserted.id };
}
The error that I’m seeing is the following:
2025-12-23T19:59:05.306Z [INFO] Workflow cancelled externally - {
sdkComponent: 'workflow',
taskQueue: '...',
namespace: '...',
workflowId: 'my-workflow-name-workflow_id',
runId: '...uuid...',
workflowType: 'myWorkflowName'
}
2025-12-23T19:59:05.306407Z WARN temporal_sdk_core::worker::workflow: Failing workflow task run_id=...uuid... failure=Failure { failure: Some(Failure { message: "Workflow cancelled", source: "TypeScriptSDK", stack_trace: "CancelledFailure: Workflow cancelled\n at RootCancellationScope.cancel (/Project/route/node_modules/@temporalio/workflow/src/cancellation-scope.ts:264:16)\n at Activator.cancelWorkflow (/Project/route/node_modules/@temporalio/workflow/src/internals.ts:481:19)\n at /Project/route/node_modules/@temporalio/workflow/src/worker-interface.ts:136:29\n at Object.activate (/Project/route/node_modules/@temporalio/workflow/src/worker-interface.ts:160:2)\n at evalmachine.<anonymous>:1:18", encoded_attributes: None, cause: None, failure_info: Some(CanceledFailureInfo(CanceledFailureInfo { details: None })) }), force_cause: Unspecified }
The only think that I can think of based on the message is that I can’t trigger an activity if the workflow has been cancelled but as I’m new in Temporal I have no idea.
Just in case I want to use the same activity when I receive a cancel signal and when I execute Request cancel from the UI.
Thanks in advance for your insights!