asLody/VirtualApp · critical · std::runtime_error
Unable to retrieve JNIEnv*.
Error message
Unable to retrieve JNIEnv*.
What it means
facebook/jni (fbjni) requires a valid JNIEnv* for the calling thread, obtained via internal::getEnv(). findClassLocal calls FindClass to resolve a Java class as a local reference; if no JNIEnv can be retrieved (no JavaVM attached or thread not attached to the JVM), it throws std::runtime_error("Unable to retrieve JNIEnv*.") before even attempting FindClass.
Solutions
- Attach the thread before calling fbjni: vm->AttachCurrentThread(&env, nullptr) (or fbjni's ThreadScope/attach helpers) at thread start
- Ensure initialize()/JNI_OnLoad ran so fbjni stored the JavaVM (g_vm); verify the library containing the fbjni initialization is loaded before use
- Use facebook::jni::ThreadScope RAII guard in worker-thread entry points to attach/detach around Java interop
- If the error surfaces in throwNewJavaException during error handling, attach the thread first, then re-raise; check that GetEnv succeeds with JNI_VERSION_1_6
Example fix
// before
void worker() {
auto cls = facebook::jni::findClassLocal("java/lang/String"); // throws: no JNIEnv
}
// after
void worker() {
JavaVM* vm = facebook::jni::Environment::current()->GetJavaVM(); // or stored g_vm
JNIEnv* env = nullptr;
vm->AttachCurrentThread(&env, nullptr);
facebook::jni::ThreadScope scope; // RAII attach/detach
auto cls = facebook::jni::findClassLocal("java/lang/String");
} Defensive patterns
Strategy: type-guard
Validate before calling
// C++: ensure the thread has a JNIEnv before fbjni calls
JNIEnv* env = nullptr;
if (g_vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
g_vm->AttachCurrentThread(&env, nullptr); // attach before fbjni usage
} Type guard
inline bool hasJniEnv() {
JNIEnv* env = nullptr;
return g_vm && g_vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) == JNI_OK;
} Try / catch
try {
auto cls = facebook::jni::findClassLocal(name);
} catch (const std::runtime_error& e) {
// "Unable to retrieve JNIEnv*." — attach the thread and retry once
attachThreadToJvm();
auto cls = facebook::jni::findClassLocal(name);
} Prevention
- Attach every native worker thread to the JVM before any fbjni call
- Use facebook::jni::ThreadScope RAII guards at thread entry points
- Ensure JNI_OnLoad / fbjni initialize() ran before first use
- Never detach a thread while Java objects from it are still referenced
- Verify GetEnv returns JNI_OK with JNI_VERSION_1_6 in debug builds
When it happens
Trigger: Calling any fbjni-backed function (findClassLocal, JObject::toString, registerNatives, throwNewJavaException, JavaClass::javaClassLocal) from a native thread that was never attached to the JVM, after AndroidJNIHelper/Initialize was skipped (no JavaVM stored), or on a thread whose JNIEnv could not be fetched with GetEnv(JNI_VERSION_1_6).
Common situations: Using fbjni from a background pthread/std::thread without calling JavaVM::AttachCurrentThread; native code running before JNI_OnLoad/initialize stored the JavaVM; a detached thread invoking Java class lookups after its attachment was released.
Understand the failure class
Background: "environment variable is not set" and "Missing keys in environment" errors: what missing required env var messages mean and how to fix them — this error's family across 28 libraries.
Related errors
- Unable to create application
- Unable to start receiver
- VirtualCore.startup() must called in main thread.
- Initializer = NULL
- ${pkg}
AI-assisted analysis of asLody/VirtualApp@666fefcb5d (2026-09-09).
Data as JSON: /api/errors/db4387d4ce0cd492.
Report an issue: GitHub.
Appendix: source
Thrown at VirtualApp/lib/src/main/jni/fb/jni/fbjni.cpp:73
return JNI_VERSION_1_6;
}
alias_ref<JClass> findClassStatic(const char* name) {
const auto env = internal::getEnv();
if (!env) {
throw std::runtime_error("Unable to retrieve JNIEnv*.");
}
local_ref<jclass> cls = adopt_local(env->FindClass(name));
FACEBOOK_JNI_THROW_EXCEPTION_IF(!cls);
auto leaking_ref = (jclass)env->NewGlobalRef(cls.get());
FACEBOOK_JNI_THROW_EXCEPTION_IF(!leaking_ref);
return wrap_alias(leaking_ref);
}
local_ref<JClass> findClassLocal(const char* name) {
const auto env = internal::getEnv();
if (!env) {
throw std::runtime_error("Unable to retrieve JNIEnv*.");
}
auto cls = env->FindClass(name);
FACEBOOK_JNI_THROW_EXCEPTION_IF(!cls);
return adopt_local(cls);
}
// jstring /////////////////////////////////////////////////////////////////////////////////////////
std::string JString::toStdString() const {
const auto env = internal::getEnv();
auto utf16String = JStringUtf16Extractor(env, self());
return detail::utf16toUTF8(utf16String.chars(), utf16String.length());
}
local_ref<JString> make_jstring(const char* utf8) {
if (!utf8) {
return {};View on GitHub (pinned to 666fefcb5d)