{"record":{"id":"079b3c085051c5c5","repo":"oracle/graal","slug":"the-vm-does-not-expose-the-required-graal-capabili","errorCode":null,"errorMessage":"The VM does not expose the required Graal capability %s.","messagePattern":"The VM does not expose the required Graal capability (.+?)\\.","errorType":"exception","errorClass":"JVMCIError","httpStatus":null,"severity":"error","filePath":"compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/api/runtime/GraalRuntime.java","lineNumber":38,"sourceCode":" *\n * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA\n * or visit www.oracle.com if you need additional information or have any\n * questions.\n */\npackage jdk.graal.compiler.api.runtime;\n\nimport jdk.vm.ci.common.JVMCIError;\n\npublic interface GraalRuntime {\n\n    String getName();\n\n    <T> T getCapability(Class<T> clazz);\n\n    default <T> T getRequiredCapability(Class<T> clazz) {\n        T ret = getCapability(clazz);\n        if (ret == null) {\n            throw new JVMCIError(\"The VM does not expose the required Graal capability %s.\", clazz.getName());\n        }\n        return ret;\n    }\n}\n","sourceCodeStart":20,"sourceCodeEnd":43,"githubUrl":"https://github.com/oracle/graal/blob/a66e9ccd1d7bf2552883939aa0788dfd0e294aab/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/api/runtime/GraalRuntime.java#L20-L43","documentation":"JNI scopes form a per-thread stack and every nested scope must use the same JNIEnv as its parent. The constructor throws IllegalStateException when a nested JNIMethodScope is opened with an env that != the env of the current top scope: mixing environments would make handle ownership and exception state incoherent, so the API rejects it immediately.","triggerScenarios":"Opening a nested JNIMethodScope with a JNIEnv obtained from a different embedded VM or a different thread's env while a scope is already active; caching an env pointer and reusing it after the thread attached to another JNIEnv; reentrant code that receives env as a parameter but ignores the enclosing scope's env.","commonSituations":"Multi-VM setups (more than one embedded JVM/Espresso instance) sharing code paths; JNI code that stashes JNIEnv globally instead of per-thread; callbacks crossing VM boundaries inside one thread.","solutions":["Always derive the nested scope's env from the enclosing scope (JNIMethodScope.scope().env()) rather than an independent env reference.","Never cache JNIEnv across threads or VMs; acquire it per-thread via GetEnv/AttachCurrentThread.","Separate per-VM call paths so scopes for different JNIEnv values never nest on the same thread."],"exampleFix":"// before\ntry (JNIMethodScope outer = new JNIMethodScope(\"outer\", envA)) {\n    try (JNIMethodScope inner = new JNIMethodScope(\"inner\", envB)) { // throws: envB != envA\n        ...\n    }\n}\n\n// after\ntry (JNIMethodScope outer = new JNIMethodScope(\"outer\", envA)) {\n    try (JNIMethodScope inner = new JNIMethodScope(\"inner\", JNIMethodScope.scope().env())) { // same env\n        ...\n    }\n}","handlingStrategy":"validation","validationCode":"// Always open nested scopes with the enclosing scope's env:\nJNIMethodScope top = JNIMethodScope.scope();\ntry (JNIMethodScope inner = new JNIMethodScope(\"inner\", top.env())) { // guaranteed same env\n    ...\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Never cache JNIEnv in statics; obtain it per thread/per VM instance.","In multi-VM setups, key all scope-entry helpers on the JNIEnv instance so different VMs cannot nest."],"tags":["jni","scope","env-mismatch","lifecycle"],"backgroundTag":null,"analyzedSha":"a66e9ccd1d7bf2552883939aa0788dfd0e294aab","analyzedAt":"2026-08-14T13:58:47.161Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}