{"record":{"id":"274b2691c9c84e93","repo":"cube-js/cube","slug":"no-cancel-handler-for-queryhandler","errorCode":null,"errorMessage":"No cancel handler for ${queryHandler}","messagePattern":"No cancel handler for (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/cubejs-query-orchestrator/src/orchestrator/QueryQueue.ts","lineNumber":1076,"sourceCode":"        queryKey: query.queryKey,\n        requestId: query.requestId,\n        error: (e.stack || e).toString(),\n        queuePrefix: this.redisQueuePrefix\n      });\n    } finally {\n      this.queueDriver.release(queueConnection);\n    }\n  }\n\n  /**\n   * Processing cancel query flow.\n   */\n  protected async processCancel(query: QueryDef, queueId: QueueId | null) {\n    const { queryHandler } = query;\n\n    try {\n      if (!this.cancelHandlers[queryHandler]) {\n        throw new Error(`No cancel handler for ${queryHandler}`);\n      }\n\n      await this.cancelHandlers[queryHandler](query);\n    } catch (e: any) {\n      this.logger('Error while cancel', {\n        queueId,\n        queryKey: query.queryKey,\n        error: e.stack || e,\n        queuePrefix: this.redisQueuePrefix,\n        requestId: query.requestId\n      });\n    }\n  }\n\n  protected redisHash(queryKey: QueryKey): QueryKeyHash {\n    return this.queueDriver.redisHash(queryKey);\n  }\n}","sourceCodeStart":1058,"sourceCodeEnd":1094,"githubUrl":"https://github.com/cube-js/cube/blob/7d981676b36392fec34088b9afab6bdcad40207c/packages/cubejs-query-orchestrator/src/orchestrator/QueryQueue.ts#L1058-L1094","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Register a cancel handler for the queryHandler name via queue.addCancelHandler(name, fn)","Only call cancelQuery for queries using the standard cancellable 'query' handler","Fix typos in the queryHandler name so it matches a registered handler"],"exampleFix":"// before\nawait queue.cancelQuery(queryWithCustomHandler); // throws: no cancel handler\n// after\nqueue.addCancelHandler('myCustomHandler', async (q) => {\n  await customRegistry.cancel(q.queryKey);\n});\nawait queue.cancelQuery(queryWithCustomHandler);","handlingStrategy":"validation","validationCode":"if (!queue.cancelHandlers?.[query.queryHandler]) { console.warn(`Cancel unsupported for handler ${query.queryHandler}`); return false; }","typeGuard":"function isCancelable(q) { return typeof q.queryHandler === 'string' && q.queryHandler in queue.cancelHandlers; }","tryCatchPattern":"try { await queue.cancelQuery(query); } catch (e) { if (e.message.startsWith('No cancel handler for')) return false; throw e; }","preventionTips":["Register addCancelHandler for every custom query handler","Only cancel queries using the standard 'query' handler","Check the cancelHandlers map before issuing cancellation"],"tags":["query-queue","cancellation","unsupported-operation"],"backgroundTag":"missing-cancel-handler","analyzedSha":"7d981676b36392fec34088b9afab6bdcad40207c","analyzedAt":"2026-09-02T03:45:10.400Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T15:18:49.778Z"}