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

  1. Attach the thread before calling fbjni: vm->AttachCurrentThread(&env, nullptr) (or fbjni's ThreadScope/attach helpers) at thread start
  2. Ensure initialize()/JNI_OnLoad ran so fbjni stored the JavaVM (g_vm); verify the library containing the fbjni initialization is loaded before use
  3. Use facebook::jni::ThreadScope RAII guard in worker-thread entry points to attach/detach around Java interop
  4. 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

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


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)