{"record":{"id":"cabb4cdb99a57268","repo":"apache/druid","slug":"item-must-either-be-a-string-or-stringtuple","errorCode":null,"errorMessage":"Item must either be a String or StringTuple","messagePattern":"Item must either be a String or StringTuple","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"processing/src/main/java/org/apache/druid/timeline/partition/PartitionBoundaries.java","lineNumber":117,"sourceCode":"      return delegate;\n    }\n  }\n\n  /**\n   * Converts the given item to a StringTuple.\n   */\n  private StringTuple toStringTuple(Object item)\n  {\n    if (item == null || item instanceof StringTuple) {\n      return (StringTuple) item;\n    } else if (item instanceof String) {\n      return StringTuple.create((String) item);\n    } else if (item instanceof String[]) {\n      return StringTuple.create((String[]) item);\n    } else if (item instanceof List) {\n      return StringTuple.create((String[]) ((List) item).toArray(new String[0]));\n    } else {\n      throw new IAE(\"Item must either be a String or StringTuple\");\n    }\n  }\n\n  @Override\n  protected List<StringTuple> delegate()\n  {\n    return delegate;\n  }\n\n  public int getNumBuckets()\n  {\n    return delegate.size() - 1;\n  }\n\n  @Override\n  public boolean equals(Object o)\n  {\n    if (this == o) {","sourceCodeStart":99,"sourceCodeEnd":135,"githubUrl":"https://github.com/apache/druid/blob/9b90983fd291f26935af934383ce360473179e4d/processing/src/main/java/org/apache/druid/timeline/partition/PartitionBoundaries.java#L99-L135","documentation":"PartitionBoundaries.toStringTuple converts the underlying set items to StringTuple for display/comparison. It only supports String, String[], and List-of-String items; any other element type triggers this IllegalArgumentException. PartitionBoundaries is typed as an immutable sorted set of Object, so a wrongly-typed element surfaces here.","triggerScenarios":"Calling toString(), or anything that iterates the boundaries through toStringTuple, on a PartitionBoundaries that was constructed (e.g. via copyOf/ImmutableSet copyOf or serialization) containing a non-String element such as byte[] or a Number.","commonSituations":"Passing raw partition boundary objects of the wrong type when rebuilding PartitionBoundaries from a payload; deserialization code that skipped the String-coercion step; tests inserting arbitrary objects into the set.","solutions":["Ensure only String, String[], or List<String> values are placed into the PartitionBoundaries set","Coerce numeric or other boundary values to strings before inserting (e.g. String.valueOf(boundary))","If boundaries come from serialized input, decode them back to Strings before constructing PartitionBoundaries","Add a constructor-time validation to reject non-string items early with a clearer message"],"exampleFix":"// before\nPartitionBoundaries boundaries = new PartitionBoundaries(ImmutableSet.of(1, 2, 3));\nString s = boundaries.toString(); // throws\n// after\nPartitionBoundaries boundaries = new PartitionBoundaries(\n    ImmutableSet.of(\"1\", \"2\", \"3\"));\nString s = boundaries.toString();","handlingStrategy":"type-guard","validationCode":"if (!(item instanceof String) && !(item instanceof String[]) && !(item instanceof List)) {\n  throw new IllegalArgumentException(\"Unsupported boundary type: \" + item.getClass());\n}","typeGuard":"static boolean isValidBoundaryItem(Object o) {\n  return o instanceof String\n      || o instanceof String[]\n      || (o instanceof List && ((List<?>) o).stream().allMatch(String.class::isInstance));\n}","tryCatchPattern":null,"preventionTips":["Only insert String/String[]/List<String> into PartitionBoundaries","Coerce numbers/other types with String.valueOf before insertion","Validate deserialized boundary payloads before building the set"],"tags":["java","illegal-argument","type-mismatch","string-tuple"],"backgroundTag":"type-mismatch","analyzedSha":"9b90983fd291f26935af934383ce360473179e4d","analyzedAt":"2026-09-07T13:32:30.957Z","contentChangedAt":"2026-09-07T13:32:30.957Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}