oracle/graal · error · ClassFormatError

Invalid constant pool tag: %s

Error message

Invalid constant pool tag: %s

What it means

ClassfileConstantPool parses the constant pool tag-by-tag; the JVMS defines a closed set of tags, and the default branch throws ClassFormatError('Invalid constant pool tag') for any tag byte outside it. This indicates malformed class-file data, not a usage error.

Source

Thrown at compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/replacements/classfile/ClassfileConstantPool.java:135

                return new ClassfileConstant.Primitive(tag, JavaConstant.forDouble(stream.readDouble()));
            case ClassfileConstant.CONSTANT_NameAndType:
                return new ClassfileConstant.NameAndType(stream);
            case ClassfileConstant.CONSTANT_Utf8:
                return new ClassfileConstant.Utf8(stream.readUTF());
            case ClassfileConstant.CONSTANT_MethodHandle:
                skipFully(stream, 3); // reference_kind, reference_index
                return new ClassfileConstant.Unsupported(tag, "CONSTANT_MethodHandle_info");
            case ClassfileConstant.CONSTANT_MethodType:
                skipFully(stream, 2); // descriptor_index
                return new ClassfileConstant.Unsupported(tag, "CONSTANT_MethodType_info");
            case ClassfileConstant.CONSTANT_Dynamic:
                skipFully(stream, 4); // bootstrap_method_attr_index, name_and_type_index
                return new ClassfileConstant.Unsupported(tag, "CONSTANT_Dynamic_info");
            case ClassfileConstant.CONSTANT_InvokeDynamic:
                skipFully(stream, 4); // bootstrap_method_attr_index, name_and_type_index
                return new ClassfileConstant.Unsupported(tag, "CONSTANT_InvokeDynamic_info");
            default:
                throw new ClassFormatError("Invalid constant pool tag: " + tag);
        }
    }

    @Override
    public int length() {
        return entries.length;
    }

    <T extends ClassfileConstant> T get(Class<T> c, int index) {
        return c.cast(entries[index]);
    }

    @Override
    public void loadReferencedType(int index, int opcode) {
        if (opcode == Bytecodes.INVOKEDYNAMIC) {
            throw new GraalError("INVOKEDYNAMIC not supported by " + ClassfileBytecodeProvider.class.getSimpleName());
        }
        entries[index].loadReferencedType(this, index, opcode);

View on GitHub (pinned to a66e9ccd1d)

Solutions

  1. Reproduce the parse outside Graal with javap -v on the class to confirm the file is malformed.
  2. Rebuild or re-download the jar containing the broken class.
  3. If you generate classes with ASM/bytecode tooling, verify the writer version and output with a verifier (e.g. CheckClassAdapter).
Defensive patterns

Strategy: try-catch

Try / catch

try {
    ClassfileConstantPool pool = new ClassfileConstantPool(stream, context);
} catch (ClassFormatError e) {
    // Malformed bytes: quarantine the artifact and rebuild/re-download it; never parse it again as-is
    throw new IllegalStateException("Corrupt class data for " + type.toJavaName(), e);
}

Prevention

When it happens

Trigger: The DataInputStream points at data that is not a well-formed constant pool: corrupt class bytes, a non-class resource misinterpreted as a class, or a class mutated by broken bytecode tooling.

Common situations: Corrupted jars (failed download, disk issue), bytecode generators computing wrong constant pool offsets, class files processed by an obfuscator/agent bug.

Related errors


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