{"record":{"id":"93162aced831a483","repo":"java-native-access/jna","slug":"callback-argument-nativeparamtypes-i-requires-custom-type","errorCode":null,"errorMessage":"Callback argument <nativeParamTypes[i]> requires custom type conversion","messagePattern":"Callback argument <nativeParamTypes\\[i\\]> requires custom type conversion","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"src/com/sun/jna/CallbackReference.java","lineNumber":314,"sourceCode":"            // to match the true Java callback method signature\n            if (mapper != null) {\n                for (int i=0;i < nativeParamTypes.length;i++) {\n                    FromNativeConverter rc = mapper.getFromNativeConverter(nativeParamTypes[i]);\n                    if (rc != null) {\n                        nativeParamTypes[i] = rc.nativeType();\n                    }\n                }\n                ToNativeConverter tn = mapper.getToNativeConverter(returnType);\n                if (tn != null) {\n                    returnType = tn.nativeType();\n                }\n            }\n            for (int i=0;i < nativeParamTypes.length;i++) {\n                nativeParamTypes[i] = getNativeType(nativeParamTypes[i]);\n                if (!isAllowableNativeType(nativeParamTypes[i])) {\n                    String msg = \"Callback argument \" + nativeParamTypes[i]\n                        + \" requires custom type conversion\";\n                    throw new IllegalArgumentException(msg);\n                }\n            }\n            returnType = getNativeType(returnType);\n            if (!isAllowableNativeType(returnType)) {\n                String msg = \"Callback return type \" + returnType\n                    + \" requires custom type conversion\";\n                throw new IllegalArgumentException(msg);\n            }\n            int flags = DLL_CALLBACK_CLASS != null\n                && DLL_CALLBACK_CLASS.isInstance(callback)\n                ? Native.CB_OPTION_IN_DLL : 0;\n            peer = Native.createNativeCallback(proxy, PROXY_CALLBACK_METHOD,\n                                               nativeParamTypes, returnType,\n                                               callingConvention, flags,\n                                               encoding);\n        }\n        cbstruct = peer != 0 ? new Pointer(peer) : null;\n        if(peer != 0) {","sourceCodeStart":296,"sourceCodeEnd":332,"githubUrl":"https://github.com/java-native-access/jna/blob/d036ad9781adad4b66693e8fa7098e4ac665e0a3/src/com/sun/jna/CallbackReference.java#L296-L332","documentation":"IllegalArgumentException thrown while registering a native callback when a callback METHOD PARAMETER type does not map to an allowable native type (see isAllowableNativeType: boolean, byte, char, short, int, long, float, double, Pointer, Structure, String/WString/Buffer-backed types, and by-reference wrappers). JNA cannot marshal an arbitrary Java object (e.g. java.lang.StringBuilder, boxed types, custom classes) into a native callback argument without a TypeMapper/custom conversion, so registration fails immediately.","triggerScenarios":"Creating a callback whose interface method declares a parameter of a type outside the allowable native set — e.g. void callback(StringBuilder sb), void callback(Integer i), or a custom POJO — when the callback is registered with the native runtime (Native.createNativeCallback path, first use in Native.loadLibrary'd function or Structure field).","commonSituations":"Copying Java signatures (java.util.List, StringBuilder, boxed Integer/Double) into a callback meant to receive C arguments; forgetting a TypeMapper on the library options for custom types; typos like using 'Integer' instead of 'int'.","solutions":["Change the callback parameter to a natively mappable type: primitive (int, double, ...), Pointer, Structure, String, or CallbackReference-supported by-reference types.","If you must receive a custom type, register a TypeMapper (e.g. via Library.OPTION_TYPE_MAPPER or Native.setTypeMapper) that provides ToNativeConverter/FromNativeConverter for that class.","For pointer-sized values use Pointer or long/PointerType instead of boxed Number subclasses; for C strings use String/WString or a byte[]/char[] buffer parameter."],"exampleFix":"// before\npublic interface ProgressCb extends Callback { void invoke(StringBuilder msg); }\n// after\npublic interface ProgressCb extends Callback { void invoke(Pointer msg); }\n// or keep String:\npublic interface ProgressCb extends Callback { void invoke(String msg); }","handlingStrategy":"validation","validationCode":"static void checkParamTypes(Method m) {\n    for (Class<?> p : m.getParameterTypes()) {\n        if (!(p.isPrimitive() || p == Pointer.class || Structure.class.isAssignableFrom(p)\n              || p == String.class || p == WString.class || p == byte[].class || p == char[].class\n              || Callback.class.isAssignableFrom(p))) {\n            throw new IllegalArgumentException(\"Unmappable callback arg type: \" + p);\n        }\n    }\n}","typeGuard":"static boolean isMappableNativeParam(Class<?> p) {\n    return p.isPrimitive() || p == Pointer.class || Structure.class.isAssignableFrom(p)\n        || p == String.class || p == WString.class;\n}","tryCatchPattern":"try { registerCallback(cb); } catch (IllegalArgumentException e) { if (e.getMessage().contains(\"requires custom type conversion\")) { /* fix signature or add TypeMapper */ } throw e; }","preventionTips":["Model C types exactly: primitives, Pointer, Structure, String.","Never use boxed or collection types in callback signatures.","Register a TypeMapper up front when custom types are needed.","Write a unit test that instantiates each callback early."],"tags":["jna","callback","type-mapping","native-marshalling"],"backgroundTag":"type-mismatch","analyzedSha":"d036ad9781adad4b66693e8fa7098e4ac665e0a3","analyzedAt":"2026-09-12T06:50:59.239Z","contentChangedAt":"2026-09-12T06:50:59.239Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}