java-native-access/jna · error · IllegalArgumentException

Callback method is inaccessible, make sure the interface is…

Error message

Callback method is inaccessible, make sure the interface is public: <callbackMethod>

What it means

IllegalArgumentException thrown by DefaultCallbackProxy's constructor when callbackMethod.isAccessible() is false and Method.setAccessible(true) fails with a SecurityException. JNA's generated proxy must invoke the callback method reflectively; if a SecurityManager or module encapsulation (JPMS strong encapsulation) blocks access to a non-public callback interface, registration of the callback cannot proceed. The message explicitly hints that making the interface public usually fixes it.

Solutions

  1. Declare the callback interface and its method public (make nested interfaces public static).
  2. If using JPMS, open/export the package: add 'exports com.example.callbacks;' or 'opens com.example.callbacks;' to module-info.java.
  3. If a SecurityManager is present, grant the JNA codebase runtime permission suppressAccessChecks, or run without the security manager in trusted environments.
  4. In OSGi, add the required AllPermission/-suppressAccessChecks via bundle policy or use fragment/extender patterns giving JNA access.

Example fix

// before
interface SecretCb extends Callback { void invoke(int code); } // package-private
// after
public interface SecretCb extends Callback { void invoke(int code); }
// JPMS alternative: add to module-info.java
exports com.example.callbacks;
Defensive patterns

Strategy: validation

Validate before calling

static void requireAccessibleCallback(Method m) {
    if (!java.lang.reflect.Modifier.isPublic(m.getDeclaringClass().getModifiers()))
        throw new IllegalArgumentException("Callback interface must be public: " + m.getDeclaringClass());
}

Type guard

static boolean isPubliclyAccessible(Class<?> iface) {
    return java.lang.reflect.Modifier.isPublic(iface.getModifiers());
}

Try / catch

try { registerCallback(cb); } catch (IllegalArgumentException e) { if (e.getMessage().contains("make sure the interface is public")) { /* make interface public or open the JPMS package */ } throw e; }

Prevention

When it happens

Trigger: Defining a callback interface (or the specific callback method) as non-public (package-private/nested without public) while a SecurityManager policy or Java module system denies setAccessible on it; running under restrictive containers (app servers, OSGi) where JNA's classloader lacks suppressAccessChecks permission; Java 9+ modules hiding a non-exported package containing the callback interface.

Common situations: Package-private callback interfaces inside library code under a SecurityManager; callback interfaces defined inside a non-open JPMS module; OSGi bundles without the required (suppressAccessChecks) DoPrivileged permission; sandboxed plugin environments.

Understand the failure class

Background: Permission denied / not authorized / 403 Forbidden: access-control rejections when the caller lacks the required role, grant, or ownership — this error's family across 18 libraries.

Related errors


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

Appendix: source

Thrown at src/com/sun/jna/CallbackReference.java:555

                toNative = NativeMappedConverter.getInstance(returnType);
            }
            else if (mapper != null) {
                toNative = mapper.getToNativeConverter(returnType);
            }
            for (int i=0;i < fromNative.length;i++) {
                if (NativeMapped.class.isAssignableFrom(argTypes[i])) {
                    fromNative[i] = new NativeMappedConverter(argTypes[i]);
                }
                else if (mapper != null) {
                    fromNative[i] = mapper.getFromNativeConverter(argTypes[i]);
                }
            }
            if (!callbackMethod.isAccessible()) {
                try {
                    callbackMethod.setAccessible(true);
                }
                catch(SecurityException e) {
                    throw new IllegalArgumentException("Callback method is inaccessible, make sure the interface is public: " + callbackMethod);
                }
            }
        }

        public Callback getCallback() {
            return CallbackReference.this.getCallback();
        }

        private Object invokeCallback(Object[] args) {
            Class<?>[] paramTypes = callbackMethod.getParameterTypes();
            Object[] callbackArgs = new Object[args.length];

            // convert basic supported types to appropriate Java parameter types
            for (int i=0;i < args.length;i++) {
                Class<?> type = paramTypes[i];
                Object arg = args[i];
                if (fromNative[i] != null) {
                    FromNativeContext context =

View on GitHub (pinned to d036ad9781)