{"record":{"id":"ed778a20e0292a91","repo":"apache/druid","slug":"unexpected-call-made-to-noopqueryprocessingpool","errorCode":null,"errorMessage":"Unexpected call made to NoopQueryProcessingPool","messagePattern":"Unexpected call made to NoopQueryProcessingPool","errorType":"exception","errorClass":"DruidException","httpStatus":null,"severity":"error","filePath":"processing/src/main/java/org/apache/druid/query/NoopQueryProcessingPool.java","lineNumber":49,"sourceCode":"\n/**\n * Implementation of {@link QueryProcessingPool} that throws when any query execution task unit is submitted to it. It is\n * semantically shutdown from the moment it is created, and since the shutdown methods are supposed to be idempotent,\n * they do not throw like the execution methods\n */\npublic class NoopQueryProcessingPool implements QueryProcessingPool\n{\n  private static final NoopQueryProcessingPool INSTANCE = new NoopQueryProcessingPool();\n\n  public static NoopQueryProcessingPool instance()\n  {\n    return INSTANCE;\n  }\n\n  @Override\n  public <T, V> ListenableFuture<T> submitRunnerTask(PrioritizedQueryRunnerCallable<T, V> task)\n  {\n    throw unsupportedException();\n  }\n\n  @Override\n  public <T, V> ListenableFuture<T> submitRunnerTask(\n      PrioritizedQueryRunnerCallable<T, V> task,\n      long timeout,\n      TimeUnit unit\n  )\n  {\n    throw unsupportedException();\n  }\n\n  @Override\n  public <T> ListenableFuture<T> submit(Callable<T> callable)\n  {\n    throw unsupportedException();\n  }\n","sourceCodeStart":31,"sourceCodeEnd":67,"githubUrl":"https://github.com/apache/druid/blob/9b90983fd291f26935af934383ce360473179e4d/processing/src/main/java/org/apache/druid/query/NoopQueryProcessingPool.java#L31-L67","documentation":"This is a Druid defensive (programming-bug) exception. NoopQueryProcessingPool is a QueryProcessingPool that is 'semantically shutdown' at creation: it exists only to satisfy dependency injection on processes that must never run query processing work, and every task-submission method throws. Hitting it means code submitted a query-runner task to a pool that is not allowed to execute anything.","triggerScenarios":"Calling NoopQueryProcessingPool.instance().submitRunnerTask(PrioritizedQueryRunnerCallable) — the two-arg overload at NoopQueryProcessingPool.java:49. On a peon (task) JVM whose task type reports no processing threads, or a router, the injected QueryProcessingPool is this noop instance, so any query execution path that submits a runner task triggers it.","commonSituations":"Running a query or sub-query on a historical/peon node type that was wired with the noop pool (e.g. tasks whose TaskConfig/peon processing config disables processing threads, or Router nodes via RouterProcessingModule); custom extensions calling queryRunnerSubmitter APIs directly; tests that inject NoopQueryProcessingPool and then exercise code that actually executes queries.","solutions":["Find the code path submitting the runner task and ensure the query never executes on this node type — route the query to a node with a real processing pool (historical/middle-manager with processing threads).","If running a peon task, ensure the task type actually needs processing threads or configure peon processing so PeonProcessingModule.getProcessingExecutorPool returns the real DruidProcessingModule pool instead of NoopQueryProcessingPool.","In tests/extensions, replace the injected NoopQueryProcessingPool with a real pool (e.g. DruidProcessingModule.createProcessingExecutorPool or a DirectQueryProcessingPool-like executor) before exercising query execution.","Treat the exception as a bug report: DruidException.defensive means an internal invariant was violated — file/report it with the stack trace to the Druid project."],"exampleFix":"// before (test/extension wiring)\nbind(QueryProcessingPool.class).toInstance(NoopQueryProcessingPool.instance());\n// then run a query -> throws\n\n// after\nfinal ExecutorService exec = Execs.multiThreaded(2, \"query-pool-%d\");\nbind(QueryProcessingPool.class).toInstance(\n    new QueryProcessingPool() { /* delegate submit methods to exec */ });","handlingStrategy":"type-guard","validationCode":"// before executing queries on this JVM\nQueryProcessingPool pool = injector.getInstance(QueryProcessingPool.class);\nif (pool instanceof NoopQueryProcessingPool) {\n  throw new IllegalStateException(\n      \"Query execution is not supported on this node; pool is NoopQueryProcessingPool\");\n}","typeGuard":"public static boolean canExecuteQueries(QueryProcessingPool pool) {\n  return !(pool instanceof NoopQueryProcessingPool) && !pool.isShutdown();\n}","tryCatchPattern":"try {\n  pool.submitRunnerTask(task);\n} catch (DruidException e) {\n  if (\"Unexpected call made to NoopQueryProcessingPool\".equals(e.getMessage())) {\n    throw new IllegalStateException(\"Query execution attempted on a node without a processing pool\", e);\n  }\n  throw e;\n}","preventionTips":["Never run query execution on node types wired with NoopQueryProcessingPool (Router, peons of task types without processing threads).","Check the injected QueryProcessingPool with instanceof NoopQueryProcessingPool before executing queries in extensions or tests.","Use a real executor-backed pool in tests that exercise query execution paths.","Treat any occurrence as an internal bug — this exception should never fire in correct code."],"tags":["druid","query-processing","defensive-exception","thread-pool","unsupported-operation"],"backgroundTag":"unsupported-operation","analyzedSha":"9b90983fd291f26935af934383ce360473179e4d","analyzedAt":"2026-09-07T13:32:30.957Z","contentChangedAt":"2026-09-07T13:32:30.957Z","schemaVersion":2},"datasetVersion":"2026-09-17T15:17:12.973Z"}