oracle/graal · error · ParserException.ClassFormatError

invalid type descriptor: {}

Error message

invalid type descriptor: {}

What it means

skipValidTypeDescriptor verifies that a valid type descriptor starts at beginIndex. The first check is bounds: if beginIndex >= descriptor.length() there is nothing to parse, and it throws ParserException.ClassFormatError 'invalid type descriptor: <desc>'.

Source

Thrown at espresso-shared/src/com.oracle.truffle.espresso.classfile/src/com/oracle/truffle/espresso/classfile/descriptors/TypeSymbols.java:217

                return forPrimitive(JavaKind.fromPrimitiveOrVoidTypeChar((char) descriptor.byteAt(beginIndex)));
            } catch (IllegalStateException e) {
                throw new ParserException.ClassFormatError("invalid descriptor: " + descriptor);
            }
        }
        return symbols.getOrCreate(descriptor.subSequence(beginIndex, endIndex));
    }

    /**
     * Verifies that a valid type descriptor is at {@code beginIndex} in {@code type}.
     *
     * @param slashes specifies if package components are separated by {@code '/'} or {@code '.'}
     * @return the index one past the valid type descriptor starting at {@code beginIndex}
     * @throws ParserException.ClassFormatError if there is no valid type descriptor
     */
    @TruffleBoundary
    static int skipValidTypeDescriptor(Symbol<? extends Descriptor> descriptor, int beginIndex, boolean slashes) throws ParserException.ClassFormatError {
        if (beginIndex >= descriptor.length()) {
            throw new ParserException.ClassFormatError("invalid type descriptor: " + descriptor);
        }
        char ch = (char) descriptor.byteAt(beginIndex);
        if (ch != '[' && ch != 'L') {
            return beginIndex + 1;
        }
        switch (ch) {
            case 'L': {
                final int endIndex = skipClassName(descriptor, beginIndex + 1, slashes ? '/' : '.');
                if (endIndex > beginIndex + 1 && endIndex < descriptor.length() && descriptor.byteAt(endIndex) == ';') {
                    return endIndex + 1;
                }
                throw new ParserException.ClassFormatError("Invalid Java name " + descriptor.subSequence(beginIndex));
            }
            case '[': {
                // compute the number of dimensions
                int index = beginIndex;
                while (index < descriptor.length() && descriptor.byteAt(index) == '[') {
                    index++;

View on GitHub (pinned to a66e9ccd1d)

Solutions

  1. Always bound-check beginIndex < descriptor.length() before each parse/skip step in your own loop
  2. Fix the driver logic that produced the out-of-range index (usually the preceding descriptor's length was wrong)
  3. Pre-validate full signatures with a complete parser rather than manual index arithmetic

Example fix

// before
int i = ...; // may equal descriptor.length()
TypeSymbols.skipValidTypeDescriptor(desc, i, true);

// after
if (i >= desc.length()) throw new ClassFormatException("signature ended unexpectedly");
TypeSymbols.skipValidTypeDescriptor(desc, i, true);
Defensive patterns

Strategy: validation

Validate before calling

if (beginIndex < 0 || beginIndex >= descriptor.length()) throw new IllegalArgumentException("beginIndex out of range: " + beginIndex);

Try / catch

catch (ParserException.ClassFormatError e) { dump descriptor plus index to pinpoint the scanning bug }

Prevention

When it happens

Trigger: Calling parse/skip with a beginIndex equal to or beyond the descriptor length — typically a scanner that already consumed the string (e.g. after ')' in a malformed signature) or an off-by-one loop bound.

Common situations: Custom signature-walking loops that advance past the end before re-checking; empty descriptor strings from failed lookups; sliced substrings that dropped the last character.

Related errors


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