java-native-access/jna · critical · java.lang.UnsatisfiedLinkError

UnsatisfiedLinkError(e.getMessage())

Error message

UnsatisfiedLinkError(e.getMessage())

What it means

During loadNativeLibrary, after successfully System.load-ing the extracted jnidispatch library, JNA attempts cleanup (deleteLibrary) of the temporary file; an IOException raised inside this try block is rethrown as UnsatisfiedLinkError with the original IOException message. The native library load itself is wrapped here, so a genuine load failure with a textual reason also surfaces this way.

Source

Thrown at src/com/sun/jna/Native.java:1124

            }

            LOG.log(DEBUG_JNA_LOAD_LEVEL, "Trying {0}", lib.getAbsolutePath());
            System.setProperty("jnidispatch.path", lib.getAbsolutePath());
            System.load(lib.getAbsolutePath());
            jnidispatchPath = lib.getAbsolutePath();
            LOG.log(DEBUG_JNA_LOAD_LEVEL, "Found jnidispatch at {0}", jnidispatchPath);

            // Attempt to delete immediately once jnidispatch is successfully
            // loaded.  This avoids the complexity of trying to do so on "exit",
            // which point can vary under different circumstances (native
            // compilation, dynamically loaded modules, normal application, etc).
            if (isUnpacked(lib)
                && !Boolean.getBoolean("jnidispatch.preserve")) {
                deleteLibrary(lib);
            }
        }
        catch(IOException e) {
            throw new UnsatisfiedLinkError(e.getMessage());
        }
    }

    /** Identify temporary files unpacked from classpath jar files. */
    static boolean isUnpacked(File file) {
        return file.getName().startsWith(JNA_TMPLIB_PREFIX);
    }

    /** Attempt to extract a native library from the current resource path,
     * using the current thread context class loader.
     * @param name Base name of native library to extract.  May also be an
     * absolute resource path (i.e. starts with "/"), in which case the
     * no transformations of the library name are performed.  If only the base
     * name is given, the resource path is attempted both with and without
     * {@link Platform#RESOURCE_PREFIX}, after mapping the library name via
     * {@link NativeLibrary#mapSharedLibraryName(String)}.
     * @return File indicating extracted resource on disk
     * @throws IOException if resource not found

View on GitHub (pinned to d036ad9781)

Solutions

  1. Read the embedded message (e.getMessage()) for the true cause - typically 'wrong ELF class' or a missing dependent library
  2. Match JVM bitness with the bundled native library (use a full official jna.jar for the platform)
  3. Set -Djna.tmpdir to a writable directory with space, or -Djnidispatch.preserve=true to keep the extracted file for diagnosis
  4. Run ldd/otool on the extracted libjnidispatch to find missing OS dependencies and install them

Example fix

// before
catch(IOException e) { /* silently lost */ }
// after
try { /* ensure writable temp */ Files.createDirectories(Paths.get(System.getProperty("jna.tmpdir", "/var/tmp/app"))); } catch (IOException e) { throw new RuntimeException("jna.tmpdir not writable: " + e.getMessage(), e); }
Defensive patterns

Strategy: try-catch

Validate before calling

// pre-checks before first JNA call
String arch = System.getProperty("os.arch");
boolean jvm64 = System.getProperty("sun.arch.data.model", "64").equals("64");
if (!jvm64 && arch.contains("64")) throw new IllegalStateException("32-bit JVM with 64-bit natives likely - match bitness");

Try / catch

try { Native.load("c", CLibrary.class); } catch (UnsatisfiedLinkError e) { String m = String.valueOf(e.getMessage()); if (m.contains("wrong ELF class") || m.contains("Can't load library") || m.contains("unable to load library")) { throw new IllegalStateException("jnidispatch load failed: " + m + " - check bitness/OS deps", e); } throw e; }

Prevention

When it happens

Trigger: System.load failing on the extracted library (wrong architecture, missing dependent shared objects, corrupt file) or an IOException during post-load temporary-file handling in loadNativeLibrary.

Common situations: Copying a 64-bit JRE onto a 32-bit libjnidispatch (or vice versa); running where required OS dependencies of jnidispatch are absent; read-only or full temp dirs causing I/O errors during unpack handling.

Understand the failure class

Background: "failed to read file", EACCES, ENOENT and "could not read <path>" errors: when a program can't read a file from disk — this error's family across 49 libraries.

Related errors


AI-assisted analysis of java-native-access/jna@d036ad9781 (2026-09-12). Data as JSON: /api/errors/993969160b1008ee. Report an issue: GitHub.