quarkusio/quarkus · error · IllegalArgumentException
Object of class ${object.getClass().getName()} has non-deter
Error message
Object of class ${object.getClass().getName()} has non-deterministic hash code, it cannot be passed as a HashMap key through the recorder boundary: ${object} What it means
The HashMap counterpart of the HashSet check: when recording a HashMap through the recorder boundary, Quarkus verifies each key overrides hashCode(). Keys inheriting Object.hashCode() have nondeterministic iteration/lookup ordering across JVM runs, which would break reproducible builds, so the build fails with this IllegalArgumentException.
Source
Thrown at core/deployment/src/main/java/io/quarkus/deployment/recording/BytecodeRecorderImpl.java:1153
//we need to create all these first, to ensure the required objects have already
//been deserialized
List<SerializationStep> setupSteps = new ArrayList<>();
List<SerializationStep> ctorSetupSteps = new ArrayList<>();
if (REPRODUCIBILITY_CHECK) {
try {
if (param instanceof Set<?> set && set.getClass() == HashSet.class) {
for (Object object : set) {
if (object.getClass().getMethod("hashCode").getDeclaringClass() == Object.class) {
throw new IllegalArgumentException("Object of class " + object.getClass().getName()
+ " has non-deterministic hash code, it cannot be passed"
+ " in a HashSet through the recorder boundary: " + object);
}
}
} else if (param instanceof Map<?, ?> map && map.getClass() == HashMap.class) {
for (Object object : map.keySet()) {
if (object.getClass().getMethod("hashCode").getDeclaringClass() == Object.class) {
throw new IllegalArgumentException("Object of class " + object.getClass().getName()
+ " has non-deterministic hash code, it cannot be passed"
+ " as a HashMap key through the recorder boundary: " + object);
}
}
}
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
}
}
boolean relaxedOk = false;
if (param instanceof Collection) {
//if this is a collection we want to serialize every element
for (Object i : (Collection) param) {
DeferredParameter val = i != null
? loadObjectInstance(i, existing, i.getClass(), relaxedValidation)
: loadObjectInstance(null, existing, Object.class, relaxedValidation);
setupSteps.add(new SerializationStep() {View on GitHub (pinned to e1c734241f)
Solutions
- Override hashCode() and equals() on the key class to be content-based
- Use a LinkedHashMap or TreeMap (with a deterministic comparator) instead of HashMap for recorded maps
- Normalize keys to Strings or other well-known hashable types before recording
- Switch to a List of key/value pairs if key-based lookup isn't needed at runtime
- As a last resort disable the reproducibility check, accepting non-reproducible builds
Example fix
// before
Map<Key, String> m = new HashMap<>(); // Key uses identity hashCode
recorder.setMap(m);
// after
class Key { String id; @Override public int hashCode() { return id.hashCode(); } @Override public boolean equals(Object o) { return o instanceof Key k && k.id.equals(id); } }
recorder.setMap(m); Defensive patterns
Strategy: type-guard
Validate before calling
static boolean hasDeterministicKeyHash(Map<?, ?> m) throws NoSuchMethodException {
for (Object k : m.keySet()) {
if (k.getClass().getMethod("hashCode").getDeclaringClass() == Object.class) return false;
}
return true;
} Type guard
static boolean overridesHashCode(Class<?> c) throws NoSuchMethodException {
return c.getMethod("hashCode").getDeclaringClass() != Object.class;
} Try / catch
try {
recorder.setMap(map);
} catch (IllegalArgumentException e) {
if (e.getMessage().contains("non-deterministic hash code")) {
recorder.setMap(new LinkedHashMap<>(map)); // preserves insertion order
} else { throw e; }
} Prevention
- Override hashCode/equals on map key classes recorded via recorders
- Prefer LinkedHashMap or TreeMap with a stable comparator for recorded maps
- Key recorded maps by Strings or primitives when possible
- Run reproducibility verification in CI to catch identity-hash keys
When it happens
Trigger: Passing a plain HashMap through a recorder method where a key class does not override hashCode(), with the reproducibility check active.
Common situations: Recording maps whose keys are custom classes or anonymous/reflection-created objects without hashCode overrides; CI reproducibility checks failing after adding a new recorded map; maps keyed by build-time-generated objects.
Related errors
- Object of class ${object.getClass().getName()} has non-deter
- A generic type is not allowed here; try creating a subclass
- Build item class must be leaf (final) types: %s
- Cannot construct empty build items
- Failed to open path tree with root %s
AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05).
Data as JSON: /api/errors/99a84085399d48b1.
Report an issue: GitHub.