quarkusio/quarkus · error · IllegalArgumentException

Value cannot be null

Error message

Value cannot be null

What it means

ValueRegistryImpl.register() validates its arguments before inserting into the backing ConcurrentHashMap. It throws IllegalArgumentException('Value cannot be null') when the value being registered is null. The registry refuses null values because a null entry would be indistinguishable from a missing key during lookup.

Source

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

 * injected into the recorders' constructors as a {@link RuntimeValue}.
 *
 * @see Application#Application(boolean)
 * @see "io.quarkus.deployment.steps.MainClassBuildStep#build for storage"
 * @see "io.quarkus.deployment.ExtensionLoader#loadStepsFrom for retrieval"
 * @see "io.quarkus.deployment.recording.ObjectLoader"
 */
public class ValueRegistryImpl implements ValueRegistry {
    private final Map<String, RuntimeInfo<?>> values = new ConcurrentHashMap<>();

    private ValueRegistryImpl() {
    }

    public <T> void register(final RuntimeKey<T> key, final T value) {
        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) {

View on GitHub (pinned to e1c734241f)

Solutions

  1. Check the value for null before calling register(); skip registration or throw a descriptive error naming the source of the null.
  2. Fix the producing code so it never returns null (substitute a default or Optional-handling).
  3. Use getOrDefault-style access patterns instead of registering sentinel nulls.

Example fix

// before
registry.register(MyKeys.TIMEOUT, config.timeout()); // config.timeout() may be null
// after
Objects.requireNonNull(config.timeout(), "timeout config missing");
registry.register(MyKeys.TIMEOUT, config.timeout());
Defensive patterns

Strategy: validation

Validate before calling

if (value == null) { throw new IllegalStateException("Refusing to register null value for " + key.key()); }
registry.register(key, value);

Type guard

boolean isRegistrable(Object key, Object value) {
    return key != null && key.key() != null && value != null;
}

Try / catch

try {
    registry.register(key, value);
} catch (IllegalArgumentException e) {
    if (e.getMessage().startsWith("Value cannot be null")) {
        log.warnf("Skipping null value for key %s", key.key());
    } else { throw e; }
}

Prevention

When it happens

Trigger: Calling RuntimeValueRegistry.register(key, null), or registerInfo with a RuntimeInfo that wraps/exposes a null value path via SimpleRuntimeInfo.of(null).

Common situations: A config value or programmatically produced bean resolves to null at startup (e.g. an optional property not set) and the code registers it unconditionally; a factory method returns null instead of throwing.

Related errors


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