Skip to content
Community

EXPLORE TOPICS

Editorial approach

New to CoreTrailKit?

Observe your services. Keep the evidence close.

CoreTrailKit
Back to articles
GUIDE/ Java

Java initialization: find the first failure

A later request reports that a class could not be initialized, but its class file is present. The useful error may have occurred earlier, when a static initializer first tried to load configuration.

By CoreTrailKit3 min read

01

Search earlier than the repeated error

Locate the first exception for the affected class, including its cause chain and the context that triggered initialization. Repeated “Could not initialize class” messages can be downstream symptoms of an earlier failure rather than evidence of a missing JAR.

Our fixture read demo.endpoint while initializing a static field. Because the property was absent, load threw IllegalStateException. The first access surfaced ExceptionInInitializerError; later use of the erroneous class failed differently.

02

Reproduce the lifecycle in one JVM

Save the following diagnostic probe as InitializationProbe.java. It intentionally catches LinkageError so it can display both attempts. That catch is for the experiment; it is not a recommendation for a production service to swallow initialization failures.

The probe changes the property after the first attempt and immediately retries access. This makes the distinction between repaired input and a previously failed class initialization visible without relying on an application framework.

java · EXAMPLE
class StaticConfig {
    static final String ENDPOINT = load();
    private static String load() {
        String value = System.getProperty("demo.endpoint");
        if (value == null) throw new IllegalStateException("demo.endpoint is missing");
        return value;
    }
}
public class InitializationProbe {
    private static void attempt() {
        try {
            System.out.println("endpoint=" + StaticConfig.ENDPOINT);
        } catch (LinkageError error) { // Diagnostic probe only.
            System.out.println(error.getClass().getSimpleName() + ":" + error.getMessage());
            if (error.getCause() != null) {
                System.out.println("cause=" + error.getCause().getClass().getSimpleName());
            }
        }
    }
    public static void main(String[] args) {
        attempt();
        System.setProperty("demo.endpoint", "https://example.invalid");
        attempt();
    }
}

03

Compare with a fresh process using valid input

Run once without the property, then in a separate JVM with the property supplied at startup. In our test, only the fresh valid process printed the endpoint on both accesses. Changing the property after the first failure did not restart initialization in the original loader.

In production, fix the configuration source and use the application’s normal recovery or deployment lifecycle. Repeated restarts without correcting the cause only repeat the failing initialization.

sh · EXAMPLE
javac -d out InitializationProbe.java
java -cp out InitializationProbe
java -Ddemo.endpoint=https://example.invalid -cp out InitializationProbe

04

Make fallible configuration loading explicit

When you control the design, load and validate configuration before constructing the application components that use it. Ordinary method failure has a clearer recovery contract than hiding fallible external configuration behind a static field initializer.

The following small example checks only presence. It demonstrates normal exception handling and a subsequent valid load; it does not validate a real endpoint’s syntax, connectivity, credentials, or health. Save it as ExplicitConfig.java.

java · EXAMPLE
public class ExplicitConfig {
    record Config(String endpoint) {}

    static Config load(String configured) {
        if (configured == null || configured.isBlank()) {
            throw new IllegalArgumentException("demo.endpoint is required");
        }
        return new Config(configured);
    }

    public static void main(String[] args) {
        boolean rejected = false;
        try { load(null); }
        catch (IllegalArgumentException expected) { rejected = true; }
        if (!rejected) throw new AssertionError("Missing configuration was accepted");
        Config config = load("https://example.invalid");
        System.out.println("explicit config=" + config.endpoint());
    }
}

05

Verify both failure reporting and recovery

Compile and run ExplicitConfig to check that missing input is rejected and a later valid input can be loaded normally. For the application, test its real configuration mechanism and startup validation separately.

Retain the original cause, class name, triggering operation, and recovery result. A valid class file, a fixed environment value, and a successfully initialized application are different observations. Confirm the final one through the application lifecycle instead of assuming that changing input repaired every cached failure.

Know the limits

Reproduced with Eclipse Temurin OpenJDK 25.0.4.1+1 in a disposable Linux container with no external network. A missing system property caused ExceptionInInitializerError. After setting it in the same JVM, a second access produced NoClassDefFoundError. Supplying it before a fresh JVM started worked; explicit configuration loading also recovered normally. Framework reload behavior was not tested.

Sources & further reading

A technical publication by CoreTrailKit.

Browse all articles

CONTINUE EXPLORING