{"record":{"id":"b8eaab78a55e8594","repo":"apache/druid","slug":"cannot-coerce-value-s-of-type-s-for-column","errorCode":null,"errorMessage":"Cannot coerce value [%s] of type [%s] for column [%s] to %s","messagePattern":"Cannot coerce value \\[(.+?)\\] of type \\[(.+?)\\] for column \\[(.+?)\\] to (.+?)","errorType":"validation","errorClass":"DruidException","httpStatus":null,"severity":"error","filePath":"processing/src/main/java/org/apache/druid/timeline/ClusterGroupTuples.java","lineNumber":107,"sourceCode":"   *   <li>{@link ClusterGroupTuples}'s compact constructor to canonicalize segment-side tuples (strict).</li>\n   *   <li>Operator-supplied rule tuples in future cluster-group partial-load matchers, which can catch the\n   *       exception and treat it as \"no match for this segment\" rather than a hard failure.</li>\n   * </ul>\n   */\n  @Nullable\n  public static Object coerceValue(String columnName, ColumnType type, @Nullable Object raw)\n  {\n    if (raw == null) {\n      return null;\n    }\n    if (ColumnType.STRING.equals(type)) {\n      return raw instanceof String ? raw : Objects.toString(raw);\n    }\n    if (ColumnType.LONG.equals(type)) {\n      if (raw instanceof Number) {\n        return ((Number) raw).longValue();\n      }\n      throw cannotCoerce(raw, columnName, \"LONG\");\n    }\n    if (ColumnType.DOUBLE.equals(type)) {\n      if (raw instanceof Number) {\n        return ((Number) raw).doubleValue();\n      }\n      throw cannotCoerce(raw, columnName, \"DOUBLE\");\n    }\n    if (ColumnType.FLOAT.equals(type)) {\n      if (raw instanceof Number) {\n        return ((Number) raw).floatValue();\n      }\n      throw cannotCoerce(raw, columnName, \"FLOAT\");\n    }\n    throw InvalidInput.exception(\n        \"Unsupported clustering column type [%s] for column [%s]; supported types are STRING, LONG, DOUBLE, FLOAT\",\n        type,\n        columnName\n    );","sourceCodeStart":89,"sourceCodeEnd":125,"githubUrl":"https://github.com/apache/druid/blob/9b90983fd291f26935af934383ce360473179e4d/processing/src/main/java/org/apache/druid/timeline/ClusterGroupTuples.java#L89-L125","documentation":"ClusterGroupTuples.coerceValue canonicalizes clustering-column tuple values to their declared ColumnType so equality works across the JSON/programmatic boundary. For LONG columns it only accepts Number instances and calls longValue(); anything else (String, Boolean, etc.) triggers InvalidInput 'Cannot coerce value ... to LONG'. Strings are deliberately not parsed to avoid silently accepting operator typos.","triggerScenarios":"Constructing ClusterGroupTuples (via constructor or Jackson deserialization) whose tuple value for a LONG clustering column is a non-Number, e.g. a JSON string \"42\" or boolean where a numeric JSON value 42 is required; canonicalizeTuples calls coerceValue and throws.","commonSituations":"Hand-authored partial-load rule JSON with quoted numbers (\"42\" instead of 42); rule files produced by tools that stringify values; clustering columns declared LONG in the segment signature but rule tuples authored as strings.","solutions":["Change the tuple value to a JSON number for LONG columns: use 42 not \"42\"","Fix the rule/JSON authoring tool to emit typed numbers instead of strings","If values truly are numeric strings, parse them at authoring time (e.g. Long.parseLong) before constructing ClusterGroupTuples; the library intentionally will not parse them"],"exampleFix":"// before (rule JSON)\n{\"tuples\": [[\"1000\"]]}\n// after\n{\"tuples\": [[1000]]}","handlingStrategy":"validation","validationCode":"Object v = tuple.get(i);\nif (v != null && \"LONG\".equals(declaredType) && !(v instanceof Number)) {\n  throw new IllegalArgumentException(\n      \"Column \" + name + \" expects a JSON number (LONG), got: \" + v.getClass().getSimpleName());\n}","typeGuard":"boolean isTypedNumber(Object v) {\n  return v instanceof Number;\n}","tryCatchPattern":"try {\n  ClusterGroupTuples ct = new ClusterGroupTuples(signature, virtualColumns, tuples);\n} catch (InvalidInput e) {\n  if (e.getMessage() != null && e.getMessage().contains(\"Cannot coerce value\")) {\n    log.error(\"Rule tuple has wrong JSON type (quoted number?); fix to numeric literal\");\n    return null; // treat as no-match per documented rule-matcher guidance\n  }\n  throw e;\n}","preventionTips":["Author rule JSON with unquoted numeric literals for LONG/DOUBLE/FLOAT columns","Validate rule files against the segment RowSignature before submitting","Never stringify numbers when serializing cluster-group tuples; keep Jackson's numeric typing intact"],"tags":["druid","type-coercion","json","clustering"],"backgroundTag":"type-mismatch","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"}