oracle/graal · error · AnnotationFormatError

Could not parse bytes for type annotations

Error message

Could not parse bytes for type annotations

What it means

JNIMethodScope keeps a per-thread stack of active JNI scopes in a ThreadLocal. scope() returns the innermost scope but throws IllegalStateException if the thread currently has no open scope: the calling code is executing JNI helper logic outside any registered native-method call. The scope must be entered first with new JNIMethodScope(name, env) (typically try-with-resources).

Source

Thrown at compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/annotation/TypeAnnotationValueParser.java:160

                short length = buf.getShort();
                for (int i = 0; i < length; ++i) {
                    buf.getShort();
                    buf.getShort();
                    buf.getShort();
                }
                break;
            }
            case CAST:
            case CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT:
            case METHOD_INVOCATION_TYPE_ARGUMENT:
            case CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT:
            case METHOD_REFERENCE_TYPE_ARGUMENT: {
                buf.getShort();
                buf.get();
                break;
            }
            default:
                throw new AnnotationFormatError("Could not parse bytes for type annotations");
        }
        int endPos = buf.position();
        byte[] targetInfo = new byte[endPos - startPos];
        buf.position(startPos).get(targetInfo).position(endPos);
        return targetInfo;
    }

    private static byte[] parseTypePath(ByteBuffer buf) {
        int startPos = buf.position();
        int depth = buf.get() & 0xFF;
        for (int i = 0; i < depth; i++) {
            buf.get();
            buf.get();
        }
        int endPos = buf.position();
        byte[] typePath = new byte[endPos - startPos];
        buf.position(startPos).get(typePath).position(endPos);
        return typePath;

View on GitHub (pinned to a66e9ccd1d)

Solutions

  1. Wrap the JNI work in try (JNIMethodScope scope = new JNIMethodScope("name", env)) { ... } before any code that calls scope().
  2. For optional lookup use currentScope()-style access (the null-returning variant shown above scope()) and only fall back to scope() when non-null.
  3. Ensure the same thread that enters the scope performs the JNI work; pass the scope explicitly to helpers if needed.

Example fix

// before
JNIMethodScope s = JNIMethodScope.scope(); // throws if no scope open
handleCall(env);

// after
try (JNIMethodScope s = new JNIMethodScope("handleCall", env)) {
    handleCall(env); // helpers inside may call JNIMethodScope.scope()
}
Defensive patterns

Strategy: validation

Validate before calling

// Use the null-tolerant current-scope accessor before calling scope():
// (the class exposes leaf lookup; if you only have scope(), guard by tracking your own enter/exit)
boolean inScope = myScopeDepth > 0; // maintained by your try-with-resources wrapper
if (!inScope) throw new IllegalStateException("JNI helper called outside a JNIMethodScope");

Try / catch

try {
    JNIMethodScope s = JNIMethodScope.scope();
} catch (IllegalStateException e) {
    // scope not opened on this thread: open one (try-with-resources) and retry once
}

Prevention

When it happens

Trigger: Calling JNIMethodScope.scope() (directly or via JNIUtil helpers that need the current scope) from a thread that never opened a scope; calling it after close() popped the last scope; invoking scope-dependent helpers from a helper/GC thread rather than the JNI-calling thread.

Common situations: Refactoring JNI glue so a helper runs before the scope is opened; asynchronous callbacks delivered on a different thread than the one that entered the scope; unit tests calling scope-dependent utilities without setup.

Related errors


AI-assisted analysis of oracle/graal@a66e9ccd1d (2026-08-14). Data as JSON: /api/errors/956281395dc4e9dc. Report an issue: GitHub.