{"record":{"id":"7aa8277f20956d95","repo":"java-native-access/jna","slug":"jna-could-not-detach-thread-after-callback-init","errorCode":null,"errorMessage":"JNA: could not detach thread after callback init\n","messagePattern":"JNA: could not detach thread after callback init\n","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"native/dispatch.c","lineNumber":1180,"sourceCode":"    if (!(*env)->IsSameObject(env, cbobj, NULL)) {\n      jobject argsobj = newJavaStructure(env, args, classAttachOptions);\n      group = (*env)->CallStaticObjectMethod(env, classCallbackReference,\n                                             MID_CallbackReference_initializeThread,\n                                             cbobj, argsobj);\n      if (group != NULL) {\n        group = (*env)->NewWeakGlobalRef(env, group);\n      }\n      if (args->name != NULL) {\n        // Make a copy, since the Java Structure which owns this native memory\n        // will go out of scope and be available for GC\n        args->name = STRDUP(args->name);\n      }\n    }\n  }\n  (*env)->PopLocalFrame(env, NULL);\n  if (!attached) {\n    if ((*jvm)->DetachCurrentThread(jvm) != 0) {\n      fprintf(stderr, \"JNA: could not detach thread after callback init\\n\");\n    }\n  }\n\n  return group;\n}\n\njclass\ngetNativeType(JNIEnv* env, jclass cls) {\n  return (*env)->CallStaticObjectMethod(env, classNative,\n                                        MID_Native_nativeType, cls);\n}\n\njclass\ngetNativeTypeMapped(JNIEnv* env, jobject converter) {\n  return (*env)->CallObjectMethod(env, converter,\n                                          MID_FromNativeConverter_nativeType);\n}\n","sourceCodeStart":1162,"sourceCodeEnd":1198,"githubUrl":"https://github.com/java-native-access/jna/blob/d036ad9781adad4b66693e8fa7098e4ac665e0a3/native/dispatch.c#L1162-L1198","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","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."],"exampleFix":"// before: native thread that JNA must attach/detach itself\npthread_create(&tid, NULL, register_jna_callback, NULL);\n// after: use a JVM thread (Executors) so attach/detach is never needed\nExecutorService pool = Executors.newSingleThreadExecutor();\npool.submit(() -> nativeLib.registerCallback(cb));","handlingStrategy":"fallback","validationCode":null,"typeGuard":null,"tryCatchPattern":null,"preventionTips":["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."],"tags":["jni","jna","native","threads","callbacks"],"backgroundTag":"jni-thread-detach-failed","analyzedSha":"d036ad9781adad4b66693e8fa7098e4ac665e0a3","analyzedAt":"2026-09-12T06:50:59.239Z","contentChangedAt":"2026-09-12T06:50:59.239Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}