java-native-access/jna · critical
JNA: Can't attach native thread to VM for callback: %d (chec
Error message
JNA: Can't attach native thread to VM for callback: %d (check stacksize for callbacks)
What it means
dispatch_callback must attach a native (non-Java) thread to the JVM before it can invoke the Java callback. If AttachCurrentThread fails, JNA frees the thread-name/group resources, prints this message including the JNI status code, and silently returns — the Java callback is never invoked and the native call proceeds without its result.
Source
Thrown at native/callback.c:721
options.detach = JNI_TRUE; // default detach behavior
options.name = NULL;
args.group = initializeThread(cb, &options);
daemon = options.daemon ? JNI_TRUE : JNI_FALSE;
needs_detach = options.detach ? JNI_TRUE : JNI_FALSE;
args.name = options.name;
}
if (daemon) {
attach_status = (*jvm)->AttachCurrentThreadAsDaemon(jvm, (void*)&env, &args);
}
else {
attach_status = (*jvm)->AttachCurrentThread(jvm, (void *)&env, &args);
}
if (attach_status != JNI_OK) {
free((void *)args.name);
if (args.group) {
(*env)->DeleteWeakGlobalRef(env, args.group);
}
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;View on GitHub (pinned to d036ad9781)
Solutions
- Increase the native thread's stack size when creating it (e.g. pthread_attr_setstacksize / CreateThread dwStackSize), or raise -Xss.
- Verify the JVM is still live and not shutting down when the native thread calls back.
- Ensure jvm version passed to GetEnv/AttachCurrentThread matches (JNI_VERSION_1_4) and the JVM pointer is valid.
- Reduce native stack depth before invoking the callback (invoke on a shallower stack).
Example fix
// before pthread_create(&tid, NULL, native_worker, ctx); // 64KB stack, too small // after pthread_attr_t a; pthread_attr_init(&a); pthread_attr_setstacksize(&a, 1 << 20); // >= 1MB pthread_create(&tid, &a, native_worker, ctx);
Defensive patterns
Strategy: validation
Validate before calling
// Before registering a callback on a native thread, ensure adequate stack: // pthread: pthread_attr_setstacksize(&attr, 1<<20) // Windows: CreateThread(NULL, 1<<20, ...) // and confirm the JVM is up: JNIEnv via GetEnv != JNI_EDETACHED failure path
Prevention
- Create native threads with >= 1MB stack when they will invoke Java callbacks
- Avoid deep native recursion before invoking callbacks
- Never call callbacks during JVM shutdown
- Raise -Xss if many threads attach with deep stacks
When it happens
Trigger: A callback invoked on a native thread with insufficient stack space (JNI cannot create the attachment bookkeeping), or the JVM refusing attachment (shutting down, wrong JNI version), yielding attach_status != JNI_OK.
Common situations: Callbacks fired on deep native call stacks or tiny-stack threads (common on Windows with small default thread stacks, hence the 'check stacksize' hint), attaching during JVM shutdown, more attached threads than -Xss/native limits allow.
Related errors
- JNA: Can't attach native thread to VM for callback thread in
- JNA: could not detach native thread (automatic)
- JNA: couldn't obtain thread-local storage
- JNA: could not detach thread
- JNA: Can't attach native thread to VM for closure handler
AI-assisted analysis of java-native-access/jna@d036ad9781 (2026-09-12).
Data as JSON: /api/errors/11affcc668c14d4a.
Report an issue: GitHub.