oracle/graal · error · ParserException.ClassFormatError

Invalid Java name {}

Error message

Invalid Java name {}

What it means

When a descriptor starts with 'L', skipValidTypeDescriptor requires a class name of at least one character terminated by ';' within the descriptor. If skipClassName returns an empty name, hits the end, or the terminating ';' is missing, it throws ParserException.ClassFormatError 'Invalid Java name <rest-of-descriptor>'.

Source

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

     * @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++;
                }
                final int dimensions = index - beginIndex;
                if (dimensions > 255) {
                    throw new ParserException.ClassFormatError("Array with more than 255 dimensions " + descriptor.subSequence(beginIndex));
                }
                return skipValidTypeDescriptor(descriptor, index, slashes);
            }
        }
        throw new ParserException.ClassFormatError("Invalid type descriptor " + descriptor.subSequence(beginIndex));
    }

    /**

View on GitHub (pinned to a66e9ccd1d)

Solutions

  1. End every reference descriptor with ';' and use '/' separators: 'Ljava/lang/String;'
  2. Build descriptors from Class.getName().replace('.', '/') inside 'L...;' rather than string templates
  3. Add a regex pre-check for reference descriptors: ^L[^;.\[]+;$

Example fix

// before
String desc = "L" + cls.getName().replace('.', '/') ; // missing ';'

// after
String desc = "L" + cls.getName().replace('.', '/') + ";";
Defensive patterns

Strategy: validation

Validate before calling

if (!desc.matches("L[^;.\[]+;")) throw new IllegalArgumentException("Bad reference descriptor (need L<name>; with '/' separators): " + desc);

Type guard

static boolean isReferenceDescriptor(String d) { return d != null && d.matches("L[^;.\[]+;"); }

Try / catch

catch (ParserException.ClassFormatError e) { point at the missing ';' or empty name in the reported descriptor }

Prevention

When it happens

Trigger: Descriptors like 'Ljava/lang/String' (no ';'), 'L;' (empty class name), or a name that runs to end-of-string without termination; also unbalanced slicing where the ';' fell outside the subSequence handed to the parser.

Common situations: Hand-assembled descriptors in code generators forgetting the ';'; strings built from Class.getName() (which uses '.' not '/') without replacing dots with slashes; truncation of the final character during string manipulation.

Related errors


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