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

  1. Override hashCode() and equals() on the key class to be content-based
  2. Use a LinkedHashMap or TreeMap (with a deterministic comparator) instead of HashMap for recorded maps
  3. Normalize keys to Strings or other well-known hashable types before recording
  4. Switch to a List of key/value pairs if key-based lookup isn't needed at runtime
  5. 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

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


AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05). Data as JSON: /api/errors/99a84085399d48b1. Report an issue: GitHub.