google/gson · error · ClassCastException

${key.getClass().getName()} is not Comparable

Error message

${key.getClass().getName()} is not Comparable

What it means

LinkedTreeMap in natural-ordering mode (no comparator) requires keys that implement Comparable. When the first key is inserted into an empty tree and the key is not instanceof Comparable, a ClassCastException is thrown (LinkedTreeMap.java:183) because there is no way to order it.

Source

Thrown at gson/src/main/java/com/google/gson/internal/LinkedTreeMap.java:184

          break;
        }

        nearest = child;
      }
    }

    // The key doesn't exist in this tree.
    if (!create) {
      return null;
    }

    // Create the node and add it to the tree or the table.
    Node<K, V> header = this.header;
    Node<K, V> created;
    if (nearest == null) {
      // Check that the value is comparable if we didn't do any comparisons.
      if (comparator == null && !(key instanceof Comparable)) {
        throw new ClassCastException(key.getClass().getName() + " is not Comparable");
      }
      created = new Node<>(allowNullValues, nearest, key, header, header.prev);
      root = created;
    } else {
      created = new Node<>(allowNullValues, nearest, key, header, header.prev);
      if (comparison < 0) { // nearest.key is higher
        nearest.left = created;
      } else { // comparison > 0, nearest.key is lower
        nearest.right = created;
      }
      rebalance(nearest, true);
    }
    size++;
    modCount++;

    return created;
  }

View on GitHub (pinned to 310ac341f2)

Solutions

  1. Implement Comparable on the key type, or pass a Comparator to the LinkedTreeMap constructor: new LinkedTreeMap<>(comparator, allowNullValues).
  2. Use a HashMap keyed on a stable String/int field derived from the object instead of the object itself.
  3. If keys are heterogeneous, define a Comparator that imposes a total order over all expected key types.

Example fix

// before
class Key { String id; } // not Comparable
map.put(new Key(), value); // throws on first insert
// after (option A: Comparable)
class Key implements Comparable<Key> {
  public int compareTo(Key o){ return id.compareTo(o.id); }
}
// after (option B: comparator)
new LinkedTreeMap<Key,V>(Comparator.comparing(k -> k.id), true);
Defensive patterns

Strategy: validation

Validate before calling

if (key != null && !(key instanceof Comparable) && comparator == null) throw new ClassCastException("key must be Comparable");

Type guard

static boolean keyTypeOK(Class<?> k, Comparator<?> cmp) { return cmp != null || Comparable.class.isAssignableFrom(k); }

Try / catch

try { map.put(k, v); } catch (ClassCastException e) { /* non-comparable key */ }

Prevention

When it happens

Trigger: Calling put on an empty LinkedTreeMap (natural order) whose key class does not implement Comparable, e.g. putting a POJO or array as a key.

Common situations: Using custom domain objects as map keys without implementing Comparable; using arrays or non-Comparable wrappers as keys; mixing key types across puts that are not mutually comparable.

Related errors


AI-assisted analysis of google/gson@310ac341f2 (2026-08-10). Data as JSON: /api/errors/bb6d8e78b83a178f. Report an issue: GitHub.