quarkusio/quarkus · error · IllegalArgumentException

Key already registered ${key}

Error message

Key already registered ${key}

What it means

The registry stores one RuntimeInfo per key using putIfAbsent; if the key already exists it throws IllegalArgumentException('Key already registered <key>'). Registration is first-write-wins — re-registering is treated as a programming error rather than an update.

Source

Thrown at core/runtime/src/main/java/io/quarkus/runtime/ValueRegistryImpl.java:52

        if (key == null || key.key() == null) {
            throw new IllegalArgumentException("Key cannot be null");
        }
        if (value == null) {
            throw new IllegalArgumentException("Value cannot be null");
        }
        registerInfo(key, SimpleRuntimeInfo.of(value));
    }

    public <T> void registerInfo(final RuntimeKey<T> key, final RuntimeInfo<T> runtimeInfo) {
        if (key == null || key.key() == null) {
            throw new IllegalArgumentException("Key cannot be null");
        }
        if (runtimeInfo == null) {
            throw new IllegalArgumentException("Value cannot be null");
        }
        RuntimeInfo<?> mapValue = values.putIfAbsent(key.key(), runtimeInfo);
        if (mapValue != null) {
            throw new IllegalArgumentException("Key already registered " + key.key());
        }
    }

    public <T> boolean containsKey(final RuntimeKey<T> key) {
        return values.containsKey(key.key());
    }

    @SuppressWarnings("unchecked")
    public <T> T get(final RuntimeKey<T> key) {
        RuntimeInfo<T> runtimeInfo = (RuntimeInfo<T>) values.get(key.key());
        if (runtimeInfo == null) {
            throw new IllegalArgumentException("Key " + key.key() + " not found");
        }
        return runtimeInfo.get(this);
    }

    @SuppressWarnings("unchecked")
    public <T> T getOrDefault(final RuntimeKey<T> key, final T defaultValue) {

View on GitHub (pinned to e1c734241f)

Solutions

  1. Guard with registry.containsKey(key) before registering, or use getOrDefault-style access.
  2. Deduplicate the key: give each registrar a unique key.
  3. If intentional override is needed, use a different API (clear/replace) rather than register.

Example fix

// before
registry.register(MyKeys.POOL_SIZE, 10);
registry.register(MyKeys.POOL_SIZE, 20); // throws
// after
if (!registry.containsKey(MyKeys.POOL_SIZE)) {
    registry.register(MyKeys.POOL_SIZE, 20);
}
Defensive patterns

Strategy: validation

Validate before calling

if (!registry.containsKey(key)) {
    registry.register(key, value);
}

Type guard

boolean isUnregistered(ValueRegistryImpl registry, RuntimeKey<?> key) {
    return !registry.containsKey(key);
}

Try / catch

try {
    registry.register(key, value);
} catch (IllegalArgumentException e) {
    if (e.getMessage().startsWith("Key already registered")) {
        log.debugf("Key %s already registered; keeping existing value", key.key());
    } else { throw e; }
}

Prevention

When it happens

Trigger: Calling register()/registerInfo() twice with the same RuntimeKey, e.g. two build steps or two extension processors registering the same key, or an app restarted registration logic within one runtime instance.

Common situations: Duplicate registration from two versions of an extension on the classpath; test code re-running setup without a fresh registry; accidental copy-paste of a key constant.

Related errors


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