{"record":{"id":"61cc35c6b3ee0f97","repo":"java-native-access/jna","slug":"library-must-be-a-proxy-class","errorCode":null,"errorMessage":"Library must be a proxy class","messagePattern":"Library must be a proxy class","errorType":"exception","errorClass":"java.lang.IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"src/com/sun/jna/Native.java","lineNumber":1306,"sourceCode":"    /** Set the OS last error code.  The value will be saved on a per-thread\n     * basis.\n     */\n    public static native void setLastError(int code);\n\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    }","sourceCodeStart":1288,"sourceCodeEnd":1324,"githubUrl":"https://github.com/java-native-access/jna/blob/d036ad9781adad4b66693e8fa7098e4ac665e0a3/src/com/sun/jna/Native.java#L1288-L1324","documentation":"Native.synchronizedLibrary wraps a Library proxy so all calls are serialized, but it only works on the dynamic proxy instances returned by Native.loadLibrary/Native.load. Passing any other Library implementation (hand-written class, different proxy) fails Proxy.isProxyClass and throws this IllegalArgumentException.","triggerScenarios":"Calling Native.synchronizedLibrary(obj) where obj is a hand-rolled implementation of a Library interface, a subclass, a CGLIB/other proxy, or a library instance obtained from a different framework.","commonSituations":"Wrapping a mocked Library in tests; passing a manually implemented interface to add thread-safety; mixing libraries created by another wrapper utility; assuming synchronizedLibrary accepts any Library.","solutions":["Pass the exact object returned by Native.loadLibrary/Native.load (the JNA proxy) to synchronizedLibrary","If you need a hand-written implementation, add your own synchronization instead of using synchronizedLibrary","Create the library via Native.load(..., Library.class) first, then wrap: Native.synchronizedLibrary(lib)","Check for wrapper layers (caching frameworks) that may have replaced the proxy with another object"],"exampleFix":"// before\nMyLib impl = new MyLibImpl(); // hand-written\nLibrary sync = Native.synchronizedLibrary(impl);\n// after\nMyLib lib = Native.load(\"mylib\", MyLib.class);\nMyLib sync = (MyLib) Native.synchronizedLibrary(lib);","handlingStrategy":"type-guard","validationCode":"static Library synchronizeIfProxy(Library lib) {\n    if (!Proxy.isProxyClass(lib.getClass())) {\n        throw new IllegalArgumentException(\"Pass the proxy returned by Native.load, not \" + lib.getClass());\n    }\n    return (Library) Native.synchronizedLibrary(lib);\n}","typeGuard":"boolean isJnaLibraryProxy(Library lib) {\n    return Proxy.isProxyClass(lib.getClass())\n        && Proxy.getInvocationHandler(lib) instanceof com.sun.jna.Library.Handler;\n}","tryCatchPattern":"try { return Native.synchronizedLibrary(lib); } catch (IllegalArgumentException e) { if (String.valueOf(e.getMessage()).equals(\"Library must be a proxy class\")) { Library real = Native.load(libName, lib.getClass().asSubclass(Library.class)); return Native.synchronizedLibrary(real); } throw e; }","preventionTips":["Only wrap objects returned by Native.load/Native.loadLibrary","Never pass hand-written or test-mock Library implementations to synchronizedLibrary","Use the typeGuard above in utility code that accepts a Library"],"tags":["java","proxy","thread-safety","illegal-argument"],"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-14T11:17:12.474Z"}