cube-js/cube · error · Error
CancelToken was already canceled
Error message
CancelToken was already canceled
What it means
CancelToken.cancel() guards against double-cancellation: if the token's `canceled` flag is already true, calling cancel() again throws 'CancelToken was already canceled'. The token coordinates cancellation of deferred/queued CancelablePromises; canceling is a one-shot transition. Callers that issue cancel() more than once (e.g. from multiple event paths) hit this error.
Source
Thrown at packages/cubejs-backend-shared/src/promises.ts:61
resolve();
};
});
promise.cancel = cancel;
return promise;
}
class CancelToken {
protected readonly deferred: (() => Promise<void> | void)[] = [];
protected readonly withQueue: CancelablePromiseCancel[] = [];
protected canceled = false;
public async cancel(): Promise<void> {
if (this.canceled) {
throw new Error('CancelToken was already canceled');
}
this.canceled = true;
if (this.deferred.length) {
await Promise.all(this.deferred.map(async (queued) => queued()));
}
if (this.withQueue.length) {
await Promise.all(
this.withQueue.map((cb) => cb())
);
}
}
public defer(fn: () => Promise<void> | void): void {
this.deferred.push(fn);
}View on GitHub (pinned to 7d981676b3)
Solutions
- Check the token state before canceling, or track locally whether cancel was already issued on your side.
- Wrap cancel() in try/catch and treat the 'already canceled' error as a no-op success.
- Ensure a single owner/path is responsible for canceling a given token.
- Serialize cancellation logic (idempotent wrapper storing a promise of the first cancel).
Example fix
// before
await token.cancel();
await token.cancel(); // throws
// after
try { await token.cancel(); } catch (e) { /* already canceled: ignore */ } Defensive patterns
Strategy: try-catch
Validate before calling
// track cancellation state on your side before calling
if (!alreadyCanceled) {
await token.cancel();
alreadyCanceled = true;
} Type guard
const isAlreadyCanceled = (e: unknown): boolean => e instanceof Error && e.message === 'CancelToken was already canceled';
Try / catch
try {
await token.cancel();
} catch (e) {
if (!isAlreadyCanceled(e)) throw e; // double-cancel is treated as success
} Prevention
- Assign a single owner responsible for canceling each token.
- Make cancel paths idempotent (guard with a local flag or memoized promise).
- Avoid multiple listeners each calling cancel on the same token.
- In teardown code, always swallow the 'already canceled' error deliberately, not blindly.
When it happens
Trigger: Calling token.cancel() twice — e.g. once from a request-abort handler and again on shutdown/timeout cleanup; or racing cancel() invocations that are not synchronized (the flag check is not atomic against concurrent async callers).
Common situations: Component unmount triggers cancel while a global timeout also cancels the same token; query orchestrator restart cancels tokens already canceled; duplicate event listeners each calling cancel().
Related errors
AI-assisted analysis of cube-js/cube@7d981676b3 (2026-09-02).
Data as JSON: /api/errors/d03358a86f678bbc.
Report an issue: GitHub.