java-native-access/jna · error · java.lang.IllegalStateException

This method must be called from the static initializer of a

Error message

This method must be called from the static initializer of a class

What it means

When Native.register() infers the calling class via the SecurityManager getClassContext mechanism, the returned context array must contain the whole stack; index 3 holds the expected caller. If the stack is shallower than 4 frames, the call is not happening where JNA requires — the static initializer of the class being registered — so it throws this IllegalStateException.

Source

Thrown at src/com/sun/jna/Native.java:1640

            } catch (Throwable ex) {
                LOG.log(Level.WARNING, "Failed to invoke StackWalker#getInstance or StackWalker#walk", ex);
            }
        }

        if (securityManagerExposerConstructor != null) {
            Class<?>[] context = null;
            try {
                Object securityManagerExposer = securityManagerExposerConstructor.newInstance();
                context = (Class<?>[]) securityManagerGetClassContext.invoke(securityManagerExposer);
            } catch (Throwable ex) {
                LOG.log(Level.WARNING, "Failed to invoke SecurityManagerExposer#<init> or SecurityManagerExposer#getClassContext", ex);
            }

            if (context == null) {
                throw new IllegalStateException("The SecurityManager implementation on this platform is broken; you must explicitly provide the class to register");
            }
            if (context.length < 4) {
                throw new IllegalStateException("This method must be called from the static initializer of a class");
            }
            return context[3];
        }

        throw new IllegalStateException("Neither the StackWalker, nor the SecurityManager based getCallingClass implementation are useable; you must explicitly provide the class to register");
    }

    /**
     * Set a thread initializer for the given callback.
     * @param cb The callback to invoke
     * @param initializer The thread initializer indicates desired thread configuration when the
     * given Callback is invoked on a native thread not yet attached to the VM.
     */
    public static void setCallbackThreadInitializer(Callback cb, CallbackThreadInitializer initializer) {
        CallbackReference.setCallbackThreadInitializer(cb, initializer);
    }

    private static final Map<Class<?>, long[]> registeredClasses = new WeakHashMap<>();

View on GitHub (pinned to d036ad9781)

Solutions

  1. Place Native.register() directly in the static initializer: static { Native.register("lib"); }.
  2. Alternatively pass the class explicitly: Native.register("lib", MyClass.class), removing the frame-depth requirement.
  3. Avoid delegating registration to another class's method when relying on inference; each mapped class must register itself.
  4. If called via reflection, ensure sufficient call depth or use the explicit-class overload.

Example fix

// before
class MyLib {
    static void init() { Native.register("mylib"); }
    static { init(); } // extra frames: register not called from <clinit> at depth 3
}

// after
class MyLib {
    static { Native.register("mylib"); }
}
Defensive patterns

Strategy: type-guard

Validate before calling

// ensure registration happens in <clinit>, not via helper methods
static final boolean REGISTERED;
static {
    StackTraceElement[] st = new Throwable().getStackTrace();
    if (st.length < 4 || !"<clinit>".equals(st[1].getMethodName())) {
        throw new IllegalStateException("Call Native.register only from the static initializer");
    }
    Native.register("mylib");
    REGISTERED = true;
}

Try / catch

try {
    Native.register("mylib");
} catch (IllegalStateException e) {
    if (e.getMessage().contains("static initializer")) {
        Native.register("mylib", MyLib.class);
    } else { throw e; }
}

Prevention

When it happens

Trigger: Calling Native.register() (without an explicit class) from instance code, a regular method, a nested helper, or any frame depth where fewer than 4 stack frames exist, instead of the class's static initializer.

Common situations: Lazy initialization of the native library inside a getter or constructor, invoking register via reflection/MethodHandle with a truncated stack, or refactoring the static block into a shared utility method.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


AI-assisted analysis of java-native-access/jna@d036ad9781 (2026-09-12). Data as JSON: /api/errors/1b43afc5b88c6459. Report an issue: GitHub.