{"record":{"id":"c68d1ef98091f1d0","repo":"apache/druid","slug":"null-worker-assignment-strategy","errorCode":null,"errorMessage":"Null worker assignment strategy","messagePattern":"Null worker assignment strategy","errorType":"exception","errorClass":"NullPointerException","httpStatus":null,"severity":"error","filePath":"multi-stage-query/src/main/java/org/apache/druid/msq/kernel/WorkerAssignmentStrategy.java","lineNumber":109,"sourceCode":"\n        // To handle cases where the input stage is limited to 1 worker because it is reading 1 giant file, I think it\n        // would be better to base the number of workers on the number of rows read by the prior stage, which would\n        // allow later stages to fan out when appropriate. However, we're not currently tracking this information\n        // in a way that is accessible to the assignment strategy.\n\n        final IntSet inputStages = stageDef.getInputStageNumbers();\n        final OptionalInt maxInputStageWorkerCount = inputStages.intStream().map(stageWorkerCountMap).max();\n        final int workerCount = Math.min(stageDef.getMaxWorkerCount(), maxInputStageWorkerCount.orElse(1));\n        return slicer.sliceStatic(inputSpec, segmentPruner, workerCount);\n      }\n    }\n  };\n\n  @JsonCreator\n  public static WorkerAssignmentStrategy fromString(final String name)\n  {\n    if (name == null) {\n      throw new NullPointerException(\"Null worker assignment strategy\");\n    }\n    return valueOf(StringUtils.toUpperCase(name));\n  }\n\n  @Override\n  @JsonValue\n  public String toString()\n  {\n    return StringUtils.toLowerCase(name());\n  }\n\n  /**\n   * @param stageDef current stage definition. Contains information on max workers, input stage numbers\n   * @param inputSpec inputSpec containing information on where the input is read from\n   * @param stageWorkerCountMap map of past stage number vs number of worker inputs\n   * @param slicer creates slices of input spec based on other parameters\n   * @param maxInputFilesPerSlice hard maximum number of files per input slice\n   * @param maxInputBytesPerSlice maximum suggested bytes per input slice","sourceCodeStart":91,"sourceCodeEnd":127,"githubUrl":"https://github.com/apache/druid/blob/9b90983fd291f26935af934383ce360473179e4d/multi-stage-query/src/main/java/org/apache/druid/msq/kernel/WorkerAssignmentStrategy.java#L91-L127","documentation":"WorkerAssignmentStrategy.fromString is a @JsonCreator that converts a config string to the enum via valueOf(toUpperCase(name)), but it first rejects a null name with NPE 'Null worker assignment strategy'. It is invoked during deserialization of MSQ query/context configuration when the strategy field is present but null (explicit null, not missing).","triggerScenarios":"Submitting an MSQ query whose context contains e.g. \"maxWorkerAssignmentStrategy\": null (or the bound field set to null), or Java code calling WorkerAssignmentStrategy.fromString(null).","commonSituations":"Query templates built programmatically that set the key to null instead of omitting it; clients that serialize unset optional fields as explicit nulls; migration of older query context JSON.","solutions":["Remove the null-valued key from the query context JSON so the default strategy is used","Set the field to a valid strategy name such as \"max\" or \"autoScale\"","In Java, guard with Optional/null-check before calling fromString"],"exampleFix":"// before\ncontext.put(\"maxWorkerAssignmentStrategy\", null);\n// after\ncontext.remove(\"maxWorkerAssignmentStrategy\"); // omit to use default, or set \"max\"/\"autoScale\"","handlingStrategy":"type-guard","validationCode":"Object v = context.get(\"maxWorkerAssignmentStrategy\");\nif (v == null) { context.remove(\"maxWorkerAssignmentStrategy\"); }\nelse { WorkerAssignmentStrategy.fromString(v.toString()); }","typeGuard":"boolean hasStrategy(Map<String, Object> ctx) { Object v = ctx.get(\"maxWorkerAssignmentStrategy\"); return v != null; }","tryCatchPattern":"try { return WorkerAssignmentStrategy.fromString(name); } catch (NullPointerException e) { return WorkerAssignmentStrategy.defaultValue(); }","preventionTips":["Omit optional config keys instead of setting them to null","Prefer valid enum names (case-insensitive) like \"max\" or \"autoScale\"","Validate query context JSON against the schema before submission"],"tags":["java","null","json-deserialization","config"],"backgroundTag":"null-argument","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"}