{"record":{"id":"a4b0a9236f6abf53","repo":"java-native-access/jna","slug":"unrecognized-proxy-handler-ih","errorCode":null,"errorMessage":"Unrecognized proxy handler: ih","messagePattern":"Unrecognized proxy handler: ih","errorType":"exception","errorClass":"java.lang.IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"src/com/sun/jna/Native.java","lineNumber":1310,"sourceCode":"\n    /**\n     * Returns a synchronized (thread-safe) library backed by the specified\n     * library.  This wrapping will prevent simultaneous invocations of any\n     * functions mapped to a given {@link NativeLibrary}.  Note that the\n     * native library may still be sensitive to being called from different\n     * threads.\n     * <p>\n     * @param  library the library to be \"wrapped\" in a synchronized library.\n     * @return a synchronized view of the specified library.\n     */\n    public static Library synchronizedLibrary(final Library library) {\n        Class<?> cls = library.getClass();\n        if (!Proxy.isProxyClass(cls)) {\n            throw new IllegalArgumentException(\"Library must be a proxy class\");\n        }\n        InvocationHandler ih = Proxy.getInvocationHandler(library);\n        if (!(ih instanceof Library.Handler)) {\n            throw new IllegalArgumentException(\"Unrecognized proxy handler: \" + ih);\n        }\n        final Library.Handler handler = (Library.Handler)ih;\n        InvocationHandler newHandler = new InvocationHandler() {\n            @Override\n            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {\n                synchronized(handler.getNativeLibrary()) {\n                    return handler.invoke(library, method, args);\n                }\n            }\n        };\n        return (Library)Proxy.newProxyInstance(cls.getClassLoader(),\n                                               cls.getInterfaces(),\n                                               newHandler);\n    }\n\n    /** If running web start, determine the location of a given native\n     * library.  This value may be used to properly set\n     * <code>jna.library.path</code> so that JNA can load libraries identified","sourceCodeStart":1292,"sourceCodeEnd":1328,"githubUrl":"https://github.com/java-native-access/jna/blob/d036ad9781adad4b66693e8fa7098e4ac665e0a3/src/com/sun/jna/Native.java#L1292-L1328","documentation":"Native.getLibrary() (or a similar API taking a loaded library proxy) requires that the object be a JDK dynamic Proxy whose InvocationHandler is a com.sun.jna.Library.Handler. JNA only understands its own handler, which carries the NativeLibrary and call metadata needed to re-wrap the library. Any other InvocationHandler (or a non-proxy object that slipped past the earlier check) makes the handler unrecognizable, so JNA refuses to proceed.","triggerScenarios":"Calling Native methods that take a loaded library instance (e.g. Native.getLibrary()/Native.synchronizedLibrary-style APIs) with an object that is not the direct return value of Native.loadLibrary/Native.load, but a library instance that was wrapped in a custom java.lang.reflect.Proxy with a non-Library.Handler InvocationHandler.","commonSituations":"Wrapping a JNA library interface in a user proxy for logging/metrics/retries, passing a mock or hand-built proxy from a test, or retrieving the library from a DI framework that substituted its own proxy for the JNA proxy.","solutions":["Pass the original JNA-created proxy (the object returned by Native.load/Native.loadLibrary) instead of a re-wrapped proxy.","Apply custom behavior (logging, caching) inside a Callback implemented in the library interface rather than by wrapping the proxy.","When delegating, delegate to a Library.Handler-based proxy: create it via Native.load with the same interface and native library, and never replace its InvocationHandler.","Keep the JNA proxy in a dedicated field and pass that field to JNA APIs, using the custom wrapper only in application code."],"exampleFix":"// before\nMyLib wrapped = (MyLib) Proxy.newProxyInstance(cl,\n    new Class[]{MyLib.class}, new LoggingHandler(jnaLib));\nNative.getNativeLibrary(wrapped); // throws: handler is LoggingHandler\n\n// after\nMyLib jnaLib = Native.load(\"c\", MyLib.class);\nNative.getNativeLibrary(jnaLib); // Library.Handler, works","handlingStrategy":"type-guard","validationCode":"Object lib = ...;\nif (!Proxy.isProxyClass(lib.getClass())\n        || !(Proxy.getInvocationHandler(lib) instanceof Library.Handler)) {\n    throw new IllegalArgumentException(\"Pass the original JNA proxy, not a custom wrapper\");\n}","typeGuard":"static boolean isJnaLibraryProxy(Object o) {\n    return Proxy.isProxyClass(o.getClass())\n        && Proxy.getInvocationHandler(o) instanceof Library.Handler;\n}","tryCatchPattern":"try {\n    Native.getNativeLibrary(lib);\n} catch (IllegalArgumentException e) {\n    if (e.getMessage().startsWith(\"Unrecognized proxy handler\")) {\n        lib = Native.load(LIB_NAME, MyLib.class); // recreate from source\n    } else { throw e; }\n}","preventionTips":["Never wrap the JNA proxy in your own java.lang.reflect.Proxy before handing it back to JNA APIs.","Store the raw Native.load result in a dedicated field for JNA API use.","Add logging/metrics inside the mapped interface (default methods) instead of proxying."],"tags":["jna","proxy","invocation-handler","native-library"],"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"}