cube-js/cube · error

No cancel handler for ${queryHandler}

Error message

No cancel handler for ${queryHandler}

What it means

processCancel looks up a cancel handler registered in this.cancelHandlers keyed by the query's queryHandler name; if none was registered for that handler it throws instead of silently ignoring the cancel request. Only handlers explicitly registered via addCancelHandler (or the defaults like 'query') can be cancelled.

Source

Thrown at packages/cubejs-query-orchestrator/src/orchestrator/QueryQueue.ts:1076

        queryKey: query.queryKey,
        requestId: query.requestId,
        error: (e.stack || e).toString(),
        queuePrefix: this.redisQueuePrefix
      });
    } finally {
      this.queueDriver.release(queueConnection);
    }
  }

  /**
   * Processing cancel query flow.
   */
  protected async processCancel(query: QueryDef, queueId: QueueId | null) {
    const { queryHandler } = query;

    try {
      if (!this.cancelHandlers[queryHandler]) {
        throw new Error(`No cancel handler for ${queryHandler}`);
      }

      await this.cancelHandlers[queryHandler](query);
    } catch (e: any) {
      this.logger('Error while cancel', {
        queueId,
        queryKey: query.queryKey,
        error: e.stack || e,
        queuePrefix: this.redisQueuePrefix,
        requestId: query.requestId
      });
    }
  }

  protected redisHash(queryKey: QueryKey): QueryKeyHash {
    return this.queueDriver.redisHash(queryKey);
  }
}

View on GitHub (pinned to 7d981676b3)

Solutions

  1. Register a cancel handler for the queryHandler name via queue.addCancelHandler(name, fn)
  2. Only call cancelQuery for queries using the standard cancellable 'query' handler
  3. Fix typos in the queryHandler name so it matches a registered handler

Example fix

// before
await queue.cancelQuery(queryWithCustomHandler); // throws: no cancel handler
// after
queue.addCancelHandler('myCustomHandler', async (q) => {
  await customRegistry.cancel(q.queryKey);
});
await queue.cancelQuery(queryWithCustomHandler);
Defensive patterns

Strategy: validation

Validate before calling

if (!queue.cancelHandlers?.[query.queryHandler]) { console.warn(`Cancel unsupported for handler ${query.queryHandler}`); return false; }

Type guard

function isCancelable(q) { return typeof q.queryHandler === 'string' && q.queryHandler in queue.cancelHandlers; }

Try / catch

try { await queue.cancelQuery(query); } catch (e) { if (e.message.startsWith('No cancel handler for')) return false; throw e; }

Prevention

When it happens

Trigger: Calling cancelQuery / processCancel for a query whose queryHandler has no entry in cancelHandlers — e.g. custom handlers ('stream', pre-aggregation build handlers, user-registered names) that were never registered with addCancelHandler.

Common situations: Attempting to cancel a pre-aggregation build or streaming query through the standard cancel API; registering a custom query handler but forgetting to register its cancel counterpart; typos in handler names.

Related errors


AI-assisted analysis of cube-js/cube@7d981676b3 (2026-09-02). Data as JSON: /api/errors/274b2691c9c84e93. Report an issue: GitHub.