java-native-access/jna · error · IllegalArgumentException

Callback type must be an interface

Error message

Callback type must be an interface

What it means

IllegalArgumentException thrown by CallbackReference.getCallback(Class, Pointer, boolean) when an existing native Pointer is being re-associated with a callback, but the given Class is not an interface. JNA callbacks must be declared as interfaces (extending com.sun.jna.Callback) because JNA generates a dynamic proxy that implements them; a concrete class cannot be proxied this way.

Source

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

    /** Return a Callback associated with the given function pointer.
     * If the pointer refers to a Java callback trampoline, return the original
     * Java Callback.  Otherwise, return a proxy to the native function
     * pointer.
     * @throws IllegalStateException if the given pointer has already been
     * mapped to a callback of a different type.
     */
    public static Callback getCallback(Class<?> type, Pointer p) {
        return getCallback(type, p, false);
    }

    private static Callback getCallback(Class<?> type, Pointer p, boolean direct) {
        if (p == null) {
            return null;
        }

        if (!type.isInterface())
            throw new IllegalArgumentException("Callback type must be an interface");
        Map<Callback, CallbackReference> map = direct ? directCallbackMap : callbackMap;
        synchronized(pointerCallbackMap) {
            Reference<Callback>[] array = pointerCallbackMap.get(p);
            Callback cb = getTypeAssignableCallback(type, array);
            if (cb != null) {
                return cb;
            }
            cb = createCallback(type, p);
            pointerCallbackMap.put(p, addCallbackToArray(cb,array));

            // No CallbackReference for this callback
            map.remove(cb);
            return cb;
        }
    }

    private static Callback getTypeAssignableCallback(Class<?> type, Reference<Callback>[] array) {
        if (array != null) {

View on GitHub (pinned to d036ad9781)

Solutions

  1. Declare the callback as an interface that extends com.sun.jna.Callback and pass that interface's Class (e.g. MyCallback.class), not the implementing object's class.
  2. If you have an instance, get the interface via a helper that finds the Callback-derived interface (JNA's findCallbackClass logic) instead of using obj.getClass().
  3. Convert abstract-class callbacks to interface-based declarations, with the implementation as a separate class or anonymous class.

Example fix

// before
class MyHandler implements MyCallback { public void callback(Object[] args) {} }
Callback cb = CallbackReference.getCallback(handler.getClass(), ptr); // IllegalArgumentException
// after
interface MyHandler extends Callback { void callback(Object[] args); }
Callback cb = CallbackReference.getCallback(MyHandler.class, ptr);
Defensive patterns

Strategy: type-guard

Validate before calling

static void requireCallbackInterface(Class<?> t) {
    if (t == null || !t.isInterface() || !Callback.class.isAssignableFrom(t))
        throw new IllegalArgumentException(t + " must be an interface extending Callback");
}

Type guard

static boolean isCallbackInterface(Class<?> t) {
    return t != null && t.isInterface() && Callback.class.isAssignableFrom(t);
}

Try / catch

try { cb = CallbackReference.getCallback(type, ptr); } catch (IllegalArgumentException e) { throw new IllegalArgumentException("Pass the callback INTERFACE class, not the impl class", e); }

Prevention

When it happens

Trigger: Calling CallbackReference.getCallback(type, pointer) — e.g. via Native.getCallback or during structure-to-callback conversion — with a Class object that is a concrete class (or abstract class) rather than an interface, typically obtained via getClass() instead of the callback interface.

Common situations: Passing obj.getClass() of a class that implements a callback instead of the interface Class; defining a callback as an abstract class implementing Callback and calling native code with it; library utilities re-mapping pointers to callbacks using the wrong Class object.

Understand the failure class

Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.

Related errors


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