{"record":{"id":"a86050efbd37486a","repo":"java-native-access/jna","slug":"structure-callback-field-field-getname-must-be-an-interface","errorCode":null,"errorMessage":"Structure Callback field '\" + field.getName() + \"' must be an interface","messagePattern":"Structure Callback field '\" \\+ field\\.getName\\(\\) \\+ \"' must be an interface","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"src/com/sun/jna/Structure.java","lineNumber":1373,"sourceCode":"            StructField structField = new StructField();\n            structField.isVolatile = Modifier.isVolatile(modifiers);\n            structField.isReadOnly = Modifier.isFinal(modifiers);\n            if (structField.isReadOnly) {\n                if (!Platform.RO_FIELDS) {\n                    throw new IllegalArgumentException(\"This VM does not support read-only fields (field '\"\n                                                       + field.getName() + \"' within \" + getClass() + \")\");\n                }\n                // In J2SE VMs, this allows overriding the value of final\n                // fields\n                field.setAccessible(true);\n            }\n            structField.field = field;\n            structField.name = field.getName();\n            structField.type = type;\n\n            // Check for illegal field types\n            if (Callback.class.isAssignableFrom(type) && !type.isInterface()) {\n                throw new IllegalArgumentException(\"Structure Callback field '\"\n                                                   + field.getName()\n                                                   + \"' must be an interface\");\n            }\n            if (type.isArray()\n                && Structure.class.equals(type.getComponentType())) {\n                String msg = \"Nested Structure arrays must use a \"\n                    + \"derived Structure type so that the size of \"\n                    + \"the elements can be determined\";\n                throw new IllegalArgumentException(msg);\n            }\n\n            int fieldAlignment = 1;\n            if (!Modifier.isPublic(field.getModifiers())) {\n                continue;\n            }\n\n            Object value = getFieldValue(structField.field);\n            if (value == null && type.isArray()) {","sourceCodeStart":1355,"sourceCodeEnd":1391,"githubUrl":"https://github.com/java-native-access/jna/blob/d036ad9781adad4b66693e8fa7098e4ac665e0a3/src/com/sun/jna/Structure.java#L1355-L1391","documentation":"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.","triggerScenarios":"Declaring a Structure field whose type is a Callback subclass (abstract or concrete class implementing Callback) instead of an interface that extends Callback.","commonSituations":"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.","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"],"exampleFix":"// before\npublic abstract class MyCb implements Callback { public abstract void invoke(); }\nclass S extends Structure { public MyCb cb; }\n// after\npublic interface MyCb extends Callback { void invoke(); }\nclass S extends Structure { public MyCb cb; }","handlingStrategy":"validation","validationCode":"for (Field f : S.class.getFields()) {\n    if (Callback.class.isAssignableFrom(f.getType()) && !f.getType().isInterface()) {\n        throw new IllegalStateException(f + \" must be an interface\");\n    }\n}","typeGuard":"static boolean isValidCallbackField(Class<?> t) {\n    return !Callback.class.isAssignableFrom(t) || t.isInterface();\n}","tryCatchPattern":"try {\n    s.size();\n} catch (IllegalArgumentException e) {\n    if (e.getMessage() != null && e.getMessage().contains(\"must be an interface\")) {\n        throw new IllegalStateException(\"Define callback as interface: \" + e.getMessage(), e);\n    }\n    throw e;\n}","preventionTips":["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"],"tags":["jna","structure","callback","interface"],"backgroundTag":"invalid-argument-value","analyzedSha":"d036ad9781adad4b66693e8fa7098e4ac665e0a3","analyzedAt":"2026-09-12T06:50:59.239Z","contentChangedAt":"2026-09-12T06:50:59.239Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}