java-native-access/jna · warning
JNA: could not detach thread after callback init
Error message
JNA: could not detach thread after callback init
What it means
After initializing a callback group on a thread that was not previously attached to the JVM, JNA attaches the thread, does the work, then calls DetachCurrentThread. This message is printed to stderr when DetachCurrentThread returns non-zero, meaning the JVM refused to detach the native thread. It is a diagnostic: the callback work itself completed, but the thread remains attached to the VM, which can leak thread-local JNI resources.
Source
Thrown at native/dispatch.c:1180
if (!(*env)->IsSameObject(env, cbobj, NULL)) {
jobject argsobj = newJavaStructure(env, args, classAttachOptions);
group = (*env)->CallStaticObjectMethod(env, classCallbackReference,
MID_CallbackReference_initializeThread,
cbobj, argsobj);
if (group != NULL) {
group = (*env)->NewWeakGlobalRef(env, group);
}
if (args->name != NULL) {
// Make a copy, since the Java Structure which owns this native memory
// will go out of scope and be available for GC
args->name = STRDUP(args->name);
}
}
}
(*env)->PopLocalFrame(env, NULL);
if (!attached) {
if ((*jvm)->DetachCurrentThread(jvm) != 0) {
fprintf(stderr, "JNA: could not detach thread after callback init\n");
}
}
return group;
}
jclass
getNativeType(JNIEnv* env, jclass cls) {
return (*env)->CallStaticObjectMethod(env, classNative,
MID_Native_nativeType, cls);
}
jclass
getNativeTypeMapped(JNIEnv* env, jobject converter) {
return (*env)->CallObjectMethod(env, converter,
MID_FromNativeConverter_nativeType);
}
View on GitHub (pinned to d036ad9781)
Solutions
- Ensure the callback thread also runs Java code (or is a Java-launched thread) so the JVM manages its lifecycle and JNA never needs to attach/detach it.
- Check for other code holding JNI references on that thread and release them before the callback returns, since held references make detach fail.
- Verify the JVM is not shutting down while native callback init runs; serialize teardown after all callback activity completes.
- If detach failures are benign in your setup, redirect/inspect stderr to confirm this is the only symptom and monitor for thread leaks.
Example fix
// before: native thread that JNA must attach/detach itself pthread_create(&tid, NULL, register_jna_callback, NULL); // after: use a JVM thread (Executors) so attach/detach is never needed ExecutorService pool = Executors.newSingleThreadExecutor(); pool.submit(() -> nativeLib.registerCallback(cb));
Defensive patterns
Strategy: fallback
Prevention
- Run callback registration on Java-managed threads so JNA never attaches/detaches foreign threads.
- Watch process thread counts for slow leaks after enabling native callbacks.
- Keep stderr captured in logs to correlate detach failures with native thread pools.
When it happens
Trigger: A callback is registered/initialized from a purely native thread (never seen by the JVM); AttachCurrentThread succeeds during init but the subsequent DetachCurrentThread call fails (e.g. the thread still holds JNI local/global references or the JVM is shutting down).
Common situations: Native libraries (C/Rust/Go) invoking JNA callback setup on their own thread pools; embedding JNA in a process where the JVM is being torn down while callbacks are still registered; unusual JVM implementations that refuse detachment while monitors are held.
Related errors
- JNA: Can't attach native thread to VM for closure handler
- JNA: could not detach thread after callback handling
- JNA: Out of memory: Can't allocate local frame
- JNA: callback object has been garbage collected
- JNA: Can't attach native thread to VM on load
AI-assisted analysis of java-native-access/jna@d036ad9781 (2026-09-12).
Data as JSON: /api/errors/7aa8277f20956d95.
Report an issue: GitHub.