java-native-access/jna · error · IllegalArgumentException
Structure Callback field '" + field.getName() + "' must be a
Error message
Structure Callback field '" + field.getName() + "' must be an interface
What it means
A Structure field whose type is a Callback must be declared as an interface, because JNA dynamically implements the native trampoline for the callback via a proxy. A concrete (non-interface) Callback class cannot be proxied, so IllegalArgumentException is thrown.
Source
Thrown at src/com/sun/jna/Structure.java:1373
StructField structField = new StructField();
structField.isVolatile = Modifier.isVolatile(modifiers);
structField.isReadOnly = Modifier.isFinal(modifiers);
if (structField.isReadOnly) {
if (!Platform.RO_FIELDS) {
throw new IllegalArgumentException("This VM does not support read-only fields (field '"
+ field.getName() + "' within " + getClass() + ")");
}
// In J2SE VMs, this allows overriding the value of final
// fields
field.setAccessible(true);
}
structField.field = field;
structField.name = field.getName();
structField.type = type;
// Check for illegal field types
if (Callback.class.isAssignableFrom(type) && !type.isInterface()) {
throw new IllegalArgumentException("Structure Callback field '"
+ field.getName()
+ "' must be an interface");
}
if (type.isArray()
&& Structure.class.equals(type.getComponentType())) {
String msg = "Nested Structure arrays must use a "
+ "derived Structure type so that the size of "
+ "the elements can be determined";
throw new IllegalArgumentException(msg);
}
int fieldAlignment = 1;
if (!Modifier.isPublic(field.getModifiers())) {
continue;
}
Object value = getFieldValue(structField.field);
if (value == null && type.isArray()) {View on GitHub (pinned to d036ad9781)
Solutions
- Convert the Callback class into an interface extending com.sun.jna.Callback (or a JNA-provided sub-interface like Callback.UncaughtExceptionHandler pattern)
- Keep an abstract class only as shared helper code, but type the Structure field with the interface
- If using StdCallLibrary.StdCallCallback or similar, still define it as an interface
Example fix
// before
public abstract class MyCb implements Callback { public abstract void invoke(); }
class S extends Structure { public MyCb cb; }
// after
public interface MyCb extends Callback { void invoke(); }
class S extends Structure { public MyCb cb; } Defensive patterns
Strategy: validation
Validate before calling
for (Field f : S.class.getFields()) {
if (Callback.class.isAssignableFrom(f.getType()) && !f.getType().isInterface()) {
throw new IllegalStateException(f + " must be an interface");
}
} Type guard
static boolean isValidCallbackField(Class<?> t) {
return !Callback.class.isAssignableFrom(t) || t.isInterface();
} Try / catch
try {
s.size();
} catch (IllegalArgumentException e) {
if (e.getMessage() != null && e.getMessage().contains("must be an interface")) {
throw new IllegalStateException("Define callback as interface: " + e.getMessage(), e);
}
throw e;
} Prevention
- Always declare callbacks as interfaces extending Callback
- Never type callback fields with concrete or abstract classes
- Keep shared callback logic in default methods or helper classes
- Add layout unit tests so mis-typed fields surface early
When it happens
Trigger: Declaring a Structure field whose type is a Callback subclass (abstract or concrete class implementing Callback) instead of an interface that extends Callback.
Common situations: Refactoring a callback into an abstract base class shared by several callbacks; accidentally using a class that implements Callback as the field type; copying C function-pointer typedefs into a class rather than an interface.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- Callback type must be an interface
- <type> is not derived from com.sun.jna.Callback
- Callback argument <nativeParamTypes[i]> requires custom type
- Callback return type <returnType> requires custom type conve
- Method signature exceeds the maximum parameter count: <metho
AI-assisted analysis of java-native-access/jna@d036ad9781 (2026-09-12).
Data as JSON: /api/errors/a86050efbd37486a.
Report an issue: GitHub.