{"record":{"id":"64bc11da5d49ecb9","repo":"java-native-access/jna","slug":"jna-could-not-detach-thread","errorCode":null,"errorMessage":"JNA: could not detach thread\n","messagePattern":"JNA: could not detach thread\n","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"native/callback.c","lineNumber":756,"sourceCode":"    fprintf(stderr, \"JNA: couldn't obtain thread-local storage\\n\");\n    return;\n  }\n\n  // Give the callback glue its own local frame to ensure all local references\n  // are properly disposed\n  if ((*env)->PushLocalFrame(env, 16) < 0) {\n    fprintf(stderr, \"JNA: Out of memory: Can't allocate local frame\\n\");\n  }\n  else {\n    invoke_callback(env, cb, cif, resp, cbargs);\n    // Make note of whether the callback wants to avoid detach\n    needs_detach = tls->needs_detach && !tls->jvm_thread;\n    (*env)->PopLocalFrame(env, NULL);\n  }\n  \n  if (needs_detach) {\n    if ((*jvm)->DetachCurrentThread(jvm) != 0) {\n      fprintf(stderr, \"JNA: could not detach thread\\n\");\n    }\n  }\n}\n\nconst char* \nJNA_callback_init(JNIEnv* env) {\n#ifdef PTHREADS\n  static pthread_once_t key_once = PTHREAD_ONCE_INIT;\n  pthread_once(&key_once, make_thread_data_key);\n#endif\n\n  if (!LOAD_CREF(env, Object, \"java/lang/Object\")) return \"java.lang.Object\";\n\n  return NULL;\n}\n  \nvoid\nJNA_callback_dispose(JNIEnv* env) {","sourceCodeStart":738,"sourceCodeEnd":774,"githubUrl":"https://github.com/java-native-access/jna/blob/d036ad9781adad4b66693e8fa7098e4ac665e0a3/native/callback.c#L738-L774","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","solutions":["Ensure no outstanding JNI local/global references pin the thread when detaching (PopLocalFrame ran, callback fully returned).","Keep the thread attached intentionally (long-lived native worker) instead of attach/detach per callback.","Check for reentrancy: don't detach from within a nested callback on the same thread.","Update JNA; detach ordering was fixed in several versions."],"exampleFix":null,"handlingStrategy":"fallback","validationCode":null,"typeGuard":null,"tryCatchPattern":null,"preventionTips":["Keep long-lived native workers attached instead of attach/detach per callback","Ensure callbacks fully return (no retained local frames) before detach","Avoid nested callbacks that reenter dispatch on the same thread","Keep JNA current; detach ordering fixes exist in newer releases"],"tags":["jni","threading","detach","callback"],"backgroundTag":"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"}