{"record":{"id":"b58bb8ca7737f14c","repo":"apache/druid","slug":"key-must-be-an-instance-of-interval","errorCode":null,"errorMessage":"key must be an instance of Interval","messagePattern":"key must be an instance of Interval","errorType":"exception","errorClass":"java.lang.ClassCastException","httpStatus":null,"severity":"error","filePath":"processing/src/main/java/org/apache/druid/timeline/IntervalTreeMap.java","lineNumber":244,"sourceCode":"\n    if (cmp < 0) {\n    // Go to the left\n      oldValue = insert(node, true, interval, value);\n    } else {\n      // Go to the right\n      oldValue = insert(node, false, interval, value);\n    }\n    recomputeState(node);\n\n    //return node;\n    return oldValue;\n  }\n\n  @Override\n  public T get(Object key)\n  {\n    if (!Interval.class.isAssignableFrom(key.getClass())) {\n      throw new ClassCastException(\"key must be an instance of Interval\");\n    }\n    Interval interval = (Interval) key;\n\n    T value = null;\n    Node<T> node = root;\n    while (node != null) {\n      int cmp = compareInterval(node.getKey(), interval);\n      if (cmp == 0) {\n        value = node.value;\n        break;\n      } else if (cmp > 0) {\n        node = node.left;\n      } else {\n        node = node.right;\n      }\n    }\n    return value;\n  }","sourceCodeStart":226,"sourceCodeEnd":262,"githubUrl":"https://github.com/apache/druid/blob/9b90983fd291f26935af934383ce360473179e4d/processing/src/main/java/org/apache/druid/timeline/IntervalTreeMap.java#L226-L262","documentation":"IntervalTreeMap.get(Object) explicitly throws ClassCastException when the key passed is not an Interval instance. Because it overrides the raw Map.get(Object) contract, a key of the wrong type cannot quietly return null — it fails loudly to prevent interval lookups with accidental types (e.g. String).","triggerScenarios":"Calling timeline/IntervalTreeMap.get(key) with anything other than an Interval — e.g. get(\"2010-01-01/2010-01-02\") with a String, or a raw-typed map where the compiler cannot enforce the key type.","commonSituations":"Legacy code using raw Map types losing generic key checks; lookups with ISO-8601 strings instead of parsed Intervals; deserialized keys typed as Object in generic helper code.","solutions":["Parse the key into an org.joda.time.Interval before lookup","Fix generics usage so the map is typed Map<Interval, T> and the key cannot be non-Interval","Use lookup(Interval) style typed APIs if available instead of raw get(Object)"],"exampleFix":"// before\nObject v = timeline.get(\"2010-01-01/2010-01-02\");\n// after\nInterval key = Intervals.of(\"2010-01-01/2010-01-02\");\nObject v = timeline.get(key);","handlingStrategy":"type-guard","validationCode":"if (key instanceof Interval) { map.get(key); } else { map.get(Intervals.of(key.toString())); }","typeGuard":"static Interval asInterval(Object key) {\n  if (key instanceof Interval) { return (Interval) key; }\n  throw new IllegalArgumentException(\"expected Interval, got \" + key.getClass());\n}","tryCatchPattern":"try { map.get(rawKey); } catch (ClassCastException e) { map.get(Intervals.of(rawKey.toString())); }","preventionTips":["Always type maps as Map<Interval, T>; avoid raw types","Parse date-range strings into Interval before lookups","Prefer typed lookup helpers over raw get(Object)"],"tags":["type-mismatch","timeline"],"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"}