java-native-access/jna · error

JNA: couldn't obtain thread-local storage

Error message

JNA: couldn't obtain thread-local storage

What it means

After attaching, dispatch_callback calls get_thread_storage to fetch the thread's TLS block (jvm pointer, name, flags). If it returns NULL, JNA prints this message and returns without invoking the callback. Without TLS, JNA cannot track whether the thread needs detaching or set per-thread state for the callback glue.

Source

Thrown at native/callback.c:738

      }
      fprintf(stderr, "JNA: Can't attach native thread to VM for callback: %d (check stacksize for callbacks)\n", attach_status);
      return;
    }
    tls = get_thread_storage(env);
    if (tls) {
      snprintf(tls->name, sizeof(tls->name), "%s", args.name ? args.name : "<unconfigured native thread>");
      tls->needs_detach = needs_detach;
      tls->jvm_thread = JNI_FALSE;
    }
    // Dispose of allocated memory
    free((void *)args.name);
    if (args.group) {
      (*env)->DeleteWeakGlobalRef(env, args.group);
    }
  }

  if (!tls) {
    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");

View on GitHub (pinned to d036ad9781)

Solutions

  1. Reduce the number of DLLs/threads consuming TLS slots on Windows; check for TLS leaks in native code.
  2. Ensure JNA's native library is fully initialized (JNA_callback_init ran) before callbacks fire on that thread.
  3. Retry callback dispatch on a Java-managed thread instead of a raw native thread.
  4. Update JNA — get_thread_storage handles allocation/retry differently across versions.
Defensive patterns

Strategy: fallback

Prevention

When it happens

Trigger: TlsAlloc/tls allocation failed or the key lookup returned NULL on the just-attached native thread — e.g. TLS slots exhausted, TlsGetValue returning NULL with no stored value, or JNA native state not initialized in this thread/DLL context.

Common situations: Windows TLS slot exhaustion from many DLLs using TlsAlloc, callbacks fired on threads in a different DLL context where JNA's per-thread init never ran, native thread teardown racing with callback dispatch.

Related errors


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