java-native-access/jna · warning

JNA: could not detach thread

Error message

JNA: could not detach thread

What it means

At the end of dispatch_callback, if the thread was attached solely for this callback and the callback did not request staying attached (needs_detach), JNA calls DetachCurrentThread. A nonzero result prints this warning; the thread remains attached to the JVM, leaking the attachment until the thread exits.

Source

Thrown at native/callback.c:756

    fprintf(stderr, "JNA: couldn't obtain thread-local storage\n");
    return;
  }

  // Give the callback glue its own local frame to ensure all local references
  // are properly disposed
  if ((*env)->PushLocalFrame(env, 16) < 0) {
    fprintf(stderr, "JNA: Out of memory: Can't allocate local frame\n");
  }
  else {
    invoke_callback(env, cb, cif, resp, cbargs);
    // Make note of whether the callback wants to avoid detach
    needs_detach = tls->needs_detach && !tls->jvm_thread;
    (*env)->PopLocalFrame(env, NULL);
  }
  
  if (needs_detach) {
    if ((*jvm)->DetachCurrentThread(jvm) != 0) {
      fprintf(stderr, "JNA: could not detach thread\n");
    }
  }
}

const char* 
JNA_callback_init(JNIEnv* env) {
#ifdef PTHREADS
  static pthread_once_t key_once = PTHREAD_ONCE_INIT;
  pthread_once(&key_once, make_thread_data_key);
#endif

  if (!LOAD_CREF(env, Object, "java/lang/Object")) return "java.lang.Object";

  return NULL;
}
  
void
JNA_callback_dispose(JNIEnv* env) {

View on GitHub (pinned to d036ad9781)

Solutions

  1. Ensure no outstanding JNI local/global references pin the thread when detaching (PopLocalFrame ran, callback fully returned).
  2. Keep the thread attached intentionally (long-lived native worker) instead of attach/detach per callback.
  3. Check for reentrancy: don't detach from within a nested callback on the same thread.
  4. Update JNA; detach ordering was fixed in several versions.
Defensive patterns

Strategy: fallback

Prevention

When it happens

Trigger: DetachCurrentThread fails for a native thread that invoked a callback — typically because native frames or TLS state still reference the JVM, or detach is attempted from a context where the JVM disallows it (shutdown, still running attached code).

Common situations: Callbacks that spawn or retain JNI references on the native thread, detach attempted while still executing inside the callback frame, JVM teardown racing a late callback, non-Java threads firing callbacks asynchronously.

Related errors


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