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
- Reduce the number of DLLs/threads consuming TLS slots on Windows; check for TLS leaks in native code.
- Ensure JNA's native library is fully initialized (JNA_callback_init ran) before callbacks fire on that thread.
- Retry callback dispatch on a Java-managed thread instead of a raw native thread.
- Update JNA — get_thread_storage handles allocation/retry differently across versions.
Defensive patterns
Strategy: fallback
Prevention
- Ensure JNA's native library initializes once and only once per process
- On Windows, audit DLLs for TLS slot leaks (TlsAlloc without TlsFree)
- Dispatch callbacks on long-lived, well-initialized threads rather than ad-hoc threads
- Update JNA to get TLS robustness fixes
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
- JNA: could not detach thread
- JNA: Can't attach native thread to VM for callback thread in
- JNA: Unhandled result conversion: %d
- JNA: error while handling callback exception, continuing
- JNA: could not detach native thread (automatic)
AI-assisted analysis of java-native-access/jna@d036ad9781 (2026-09-12).
Data as JSON: /api/errors/4c618666ae5165e0.
Report an issue: GitHub.