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
- End every reference descriptor with ';' and use '/' separators: 'Ljava/lang/String;'
- Build descriptors from Class.getName().replace('.', '/') inside 'L...;' rather than string templates
- 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
- Always terminate reference descriptors with ';' and use '/' package separators
- Derive descriptors via Class.getName().replace('.', '/') instead of templates
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
- unknown primitive or void type character:
- invalid descriptor: {}
- invalid type descriptor: {}
- Array with more than 255 dimensions {}
- Invalid type descriptor {}
AI-assisted analysis of oracle/graal@a66e9ccd1d (2026-08-14).
Data as JSON: /api/errors/bdfb103fc3353334.
Report an issue: GitHub.