Cancellation
Every runner receives an AbortSignal in its context. flow.cancel() requests cancellation and returns whether an active run was found. The flow remains busy until the runner settles because aborting a request cannot undo a server write.
const flow = useSubmitFlow({
refresh: {
run: async (_input, { signal }) => {
const response = await fetch("/api/refresh", { method: "POST", signal });
if (!response.ok) throw new Error("Refresh failed");
return response.json();
},
},
});
For multi-step runners, check cancellation between steps. A dependency may ignore the signal after the request has started, but throwIfCancelled() prevents the next step from beginning.
run: async (input, context) => {
const record = await saveDraft(input, { signal: context.signal });
context.throwIfCancelled();
await publishRecord(record.id, { signal: context.signal });
return record;
};
The result is { status: 'cancelled', reason: 'user' } after an active run settles. A declined confirmation resolves with reason: 'declined'. A cancelled request is not a rollback guarantee. Try the abort-aware example.