java-native-access/jna · error

JNA: Can't attach native thread to VM on load

Error message

JNA: Can't attach native thread to VM on load

What it means

In the JNA native library's load/initialization entry point, if the loading thread is not already attached to the JVM, JNA calls AttachCurrentThread to obtain a JNIEnv for initialization. If attachment fails, it prints this message and returns 0, failing the native library load — subsequent JNA operations will fail because core IDs and method IDs were never initialized.

Source

Thrown at native/dispatch.c:3358

JNIEXPORT jstring JNICALL
Java_com_sun_jna_Native_getAPIChecksum(JNIEnv *env, jclass UNUSED(classp)) {
#ifndef CHECKSUM
#define CHECKSUM "undefined"
#endif
  return newJavaString(env, CHECKSUM, CHARSET_UTF8);
}

JNIEXPORT jint JNICALL
JNI_OnLoad(JavaVM *jvm, void *UNUSED(reserved)) {
  JNIEnv* env;
  int result = JNI_VERSION_1_4;
  int attached = (*jvm)->GetEnv(jvm, (void *)&env, JNI_VERSION_1_4) == JNI_OK;
  const char* err;

  if (!attached) {
    if ((*jvm)->AttachCurrentThread(jvm, (void *)&env, NULL) != JNI_OK) {
      fprintf(stderr, "JNA: Can't attach native thread to VM on load\n");
      return 0;
    }
  }

  if ((err = JNA_init(env)) != NULL) {
    fprintf(stderr, "JNA: Problems loading core IDs: %s\n", err);
    result = 0;
  }
  else if ((err = JNA_callback_init(env)) != NULL) {
    fprintf(stderr, "JNA: Problems loading callback IDs: %s\n", err);
    result = 0;
  }
  if (!attached) {
    if ((*jvm)->DetachCurrentThread(jvm) != 0) {
      fprintf(stderr, "JNA: could not detach thread on initial load\n");
    }
  }

View on GitHub (pinned to d036ad9781)

Solutions

  1. Load the JNA native library early in a healthy JVM (touch a JNA class on the main thread at startup) rather than lazily during teardown.
  2. Verify only one JVM instance exists in the process and the native library is not shared across JVMs.
  3. Check that no shutdown hooks or finalizers trigger JNA loading after the VM begins destruction.
  4. If the load fails, restart the JVM cleanly; subsequent JNA calls cannot work with uninitialized core IDs.

Example fix

// before: first JNA touch inside a shutdown hook
Runtime.getRuntime().addShutdownHook(new Thread(() -> Native.getLastError()));
// after: initialize JNA at startup
public static void main(String[] a) {
  Native.register(NativeLibrary.class); // force early native load
  ...
}
Defensive patterns

Strategy: validation

Validate before calling

// Force JNA native init early, on the main thread, while the JVM is healthy:
static { com.sun.jna.Native.class.getName(); /* touch JNA before any shutdown path */ }

Prevention

When it happens

Trigger: JNI_OnLoad / JNA native init runs on a thread the JVM cannot attach (VM already destroying, or a host process loading the library outside a proper JVM lifecycle), so (*jvm)->AttachCurrentThread returns != JNI_OK.

Common situations: Library loaded too late during shutdown; embedding JNA's native lib in a non-JVM process; version mismatches where the native dispatch library is loaded by a different classloader/VM instance than expected.

Related errors


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