oracle/graal · error · IllegalArgumentException
No ClassConstant at constant pool index ${cpi}
Error message
No ClassConstant at constant pool index ${cpi} What it means
IllegalArgumentException from Klass.resolveClassConstantInPool: when resolving constant pool entry cpi as a CONSTANT_Class, the entry at that index is not a class constant (ClassCastException) or the index is outside the pool (IndexOutOfBoundsException). Both are converted to 'No ClassConstant at constant pool index <cpi>'.
Source
Thrown at espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/Klass.java:1916
}
@Override
public final boolean isMagicAccessor() {
if (getJavaVersion().java23OrEarlier()) {
assert getMeta().sun_reflect_MagicAccessorImpl != null;
return getMeta().sun_reflect_MagicAccessorImpl.isAssignableFrom(this);
}
return false;
}
@Override
@TruffleBoundary
public final Klass resolveClassConstantInPool(int cpi) {
if (this instanceof ObjectKlass objectKlass) {
try {
return objectKlass.getConstantPool().resolvedKlassAt(objectKlass, cpi, false);
} catch (ClassCastException | IndexOutOfBoundsException e) {
throw new IllegalArgumentException("No ClassConstant at constant pool index " + cpi);
}
}
return null;
}
@Override
public Method lookupVTableEntry(int vtableIndex) {
ObjectKlass k;
if (this instanceof ObjectKlass) {
k = (ObjectKlass) this;
} else if (this instanceof ArrayKlass) {
k = getMeta().java_lang_Object;
} else {
// primitive.
return null;
}
assert !k.isInterface();
Method.MethodVersion[] table = k.getVTable();View on GitHub (pinned to a66e9ccd1d)
Solutions
- Validate the tag at cpi is CONSTANT_Class before resolving (Espresso's pool API exposes tag lookup).
- Re-derive indices from the same class bytes that will be resolved — never cache cp indices across recompiles.
- Bounds-check cpi against getConstantPool().length().
- Catch IllegalArgumentException at the caller and fall back to a non-constant-pool resolution path if the index is untrusted.
Example fix
// before
Klass k = klass.resolveClassConstantInPool(cpi);
// after
if (cpi < 0 || cpi >= objectKlass.getConstantPool().length() ||
objectKlass.getConstantPool().tagAt(cpi) != ConstantTag.CLASS) {
throw new IllegalArgumentException("cpi " + cpi + " is not a class constant");
}
Klass k = klass.resolveClassConstantInPool(cpi); Defensive patterns
Strategy: validation
Validate before calling
ConstantPool pool = objectKlass.getConstantPool();
if (cpi < 0 || cpi >= pool.length() || pool.tagAt(cpi) != ConstantTag.CLASS) {
throw new IllegalArgumentException("cpi " + cpi + " is not a CONSTANT_Class entry");
} Try / catch
try {
return klass.resolveClassConstantInPool(cpi);
} catch (IllegalArgumentException e) {
// index not a class constant: re-derive cpi from current class bytes or fail gracefully
} Prevention
- Never cache constant pool indices across class redefinitions/recompiles.
- Check the tag at cpi equals CONSTANT_Class before resolving.
- Bounds-check indices against pool length.
When it happens
Trigger: JVMCI/reflection-style API (resolveClassConstantInPool) invoked with a cpi that points to a String, MethodType, or other non-Class entry, or an index beyond pool length; bytecode instrumentation rewriting a class entry into a different tag without updating the caller.
Common situations: Tooling built on Espresso's Klass API (debuggers, hotswap, AOT scanners) that reads cp indices from one class version while the pool belongs to another; recompilation changing constant pool layout between the instrumentation pass and resolution.
Related errors
- Out of scratch registers: %s
- Unexpected tag: ${tag}
- Class circularity detected
- NamesDontMatch
- Authority component present
AI-assisted analysis of oracle/graal@a66e9ccd1d (2026-08-14).
Data as JSON: /api/errors/60be653113e24ecc.
Report an issue: GitHub.