quarkusio/quarkus · error · IllegalStateException
Improper registration of ZoneId substitution
Error message
Improper registration of ZoneId substitution
What it means
AdditionalSubstitutionsBuildStep registers bytecode-recorder substitutions for JDK time types. It reflects on java.time.ZoneId/ZoneRegion/ZoneOffset; if any of these classes cannot be loaded on the JVM running the build, registration is incomplete and it throws IllegalStateException('Improper registration of ZoneId substitution').
Source
Thrown at core/deployment/src/main/java/io/quarkus/deployment/recording/substitutions/AdditionalSubstitutionsBuildStep.java:31
public void additionalSubstitutions(BuildProducer<ObjectSubstitutionBuildItem> producer) {
zoneIdSubstitutions(producer);
}
@SuppressWarnings("unchecked")
private void zoneIdSubstitutions(BuildProducer<ObjectSubstitutionBuildItem> producer) {
try {
/*
* We can't refer to these classes as they are package private but we need a handle on need
* because the bytecode recorder needs to have the actual class registered and not a super class
*/
Class<ZoneId> zoneRegionClass = (Class<ZoneId>) Class.forName("java.time.ZoneRegion");
producer.produce(new ObjectSubstitutionBuildItem(zoneRegionClass, String.class, ZoneIdSubstitution.class));
Class<ZoneId> zoneOffsetClass = (Class<ZoneId>) Class.forName("java.time.ZoneOffset");
producer.produce(new ObjectSubstitutionBuildItem(zoneOffsetClass, String.class, ZoneIdSubstitution.class));
} catch (ClassNotFoundException e) {
throw new IllegalStateException("Improper registration of ZoneId substitution", e);
}
}
}
View on GitHub (pinned to e1c734241f)
Solutions
- Build with a complete standard JDK 11+ that includes full java.time in java.base
- Check the JDK with: jshell> Class.forName("java.time.ZoneRegion") — it must succeed
- Stop excluding/stripping java.time from your JDK or jlink image
- Point JAVA_HOME / Maven toolchains to a standard Temurin/Oracle/OpenJDK build
Example fix
// before (CI image) FROM eclipse-temurin:11-jre-alpine-stripped // java.time partially removed // after FROM eclipse-temurin:11-jdk // full java.base module JAVA_HOME=/usr/lib/jvm/temurin-11-jdk-amd64
Defensive patterns
Strategy: validation
Validate before calling
// run before building on a new JDK image
Class.forName("java.time.ZoneRegion");
Class.forName("java.time.ZoneOffset"); // must not throw ClassNotFoundException Prevention
- Build with a complete standard JDK 11+
- Never jlink-strip java.time out of java.base for build images
- Verify CI toolchains with Class.forName("java.time.ZoneRegion")
When it happens
Trigger: Class.forName('java.time.ZoneRegion') or 'java.time.ZoneOffset' throws ClassNotFoundException during augmentation — only possible on a non-standard/broken JDK (pre-Java-8 java.time, stripped JRE modules such as missing java.base entries, exotic runtimes).
Common situations: Building on a stripped-down JVM/JDK image missing java.time classes; using an unusual JDK vendor or a jlink-minimized runtime that removed parts of java.base; misconfigured toolchains in CI.
Related errors
- Failed to open path tree with root %s
- Quarkus applications require Java 21 or higher to build
- Dev services for ${request.getName()} requires a startable s
- Name cannot start with '/':${name}
- The class (${name}) cannot be created during deployment.
AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05).
Data as JSON: /api/errors/d32a3f100e54c86b.
Report an issue: GitHub.