apache/flink · error · FlinkRuntimeException

Could not access the MODEL$ field of avro record

Error message

Could not access the MODEL$ field of avro record

What it means

AvroFactory.getSpecificDataForClass reflectively reads the static MODEL$ field of a generated SpecificRecord class to obtain its SpecificData instance. IllegalAccessException means the field exists but the runtime forbids access — classloader isolation or module/JPMS access rules — so Flink wraps it in FlinkRuntimeException. (NoSuchFieldException is handled gracefully by falling back to new SpecificData(cl).)

Source

Thrown at flink-formats/flink-avro/src/main/java/org/apache/flink/formats/avro/typeutils/AvroFactory.java:168

        return newSchemaOptional.orElseGet(() -> specificData.getSchema(type));
    }

    /**
     * Creates a {@link SpecificData} object for a given class. Possibly uses the specific data from
     * the generated class with logical conversions applied (avro >= 1.9.x).
     *
     * <p>Copied over from {@code SpecificData#getForClass(Class<T> c)} we do not use the method
     * directly, because we want to be API backwards compatible with older Avro versions which did
     * not have this method
     */
    public static <T extends SpecificData> SpecificData getSpecificDataForClass(
            Class<T> type, ClassLoader cl) {
        try {
            Field specificDataField = type.getDeclaredField("MODEL$");
            specificDataField.setAccessible(true);
            return (SpecificData) specificDataField.get((Object) null);
        } catch (IllegalAccessException e) {
            throw new FlinkRuntimeException("Could not access the MODEL$ field of avro record", e);
        } catch (NoSuchFieldException e) {
            return new SpecificData(cl);
        }
    }

    /**
     * Extracts an Avro {@link Schema} from a {@link SpecificRecord}. We do this by creating an
     * instance of the class using the zero-argument constructor and calling {@link
     * SpecificRecord#getSchema()} on it.
     */
    private static Optional<Schema> tryExtractAvroSchemaViaInstance(Class<?> type) {
        try {
            SpecificRecord instance = (SpecificRecord) type.newInstance();
            return Optional.ofNullable(instance.getSchema());
        } catch (InstantiationException | IllegalAccessException e) {
            LOG.warn(
                    "Could not extract schema from Avro-generated SpecificRecord class {}: {}.",
                    type,

View on GitHub (pinned to 2f3c205e92)

Solutions

  1. Ensure exactly one copy of the Avro generated class (and matching avro version) is on the classpath — duplicate classes across parent/child classloaders commonly cause this.
  2. Set classloader.resolve-order to parent-first for the avro artifacts, or package avro + generated classes together consistently.
  3. On JDK 16+, add the required opens/add-opens JVM options (e.g. --add-opens java.base/java.lang=ALL-UNNAMED and opens for the record's package) if modules are in play.
  4. As a workaround, avoid the SpecificRecord path (use GenericRecord with an explicit schema).

Example fix

# before (flink-conf.yaml)
classloader.resolve-order: child-first

# after
classloader.resolve-order: parent-first
# or remove duplicate avro deps from the user jar:
<dependency><groupId>org.apache.avro</groupId><artifactId>avro</artifactId><scope>provided</scope></dependency>
Defensive patterns

Strategy: try-catch

Validate before calling

try {
    Class<?> c = Class.forName(recordClassName, false, userClassLoader);
    Field f = c.getDeclaredField("MODEL$");
    f.setAccessible(true);
} catch (NoSuchFieldException expected) {
    // fine: AvroFactory falls back to new SpecificData(cl)
} catch (IllegalAccessException e) {
    throw new IllegalStateException("Classloader/module blocks MODEL$ access; check classloader.resolve-order and duplicate avro jars", e);
}

Try / catch

try {
    SpecificData sd = AvroFactory.getSpecificDataForClass(type, cl);
    // proceed
} catch (FlinkRuntimeException e) {
    // reflective access denied: fall back to generic path with explicit schema
    datumWriter = new GenericDatumWriter<>(explicitSchema); // requires schema supplied
}

Prevention

When it happens

Trigger: A user-code classloader (e.g. Flink's child-first classloading, plugin classloader, or per-job classloader isolation) loads the Avro generated class in a way that setAccessible(true) on MODEL$ is denied; also Java module-system boundaries denying deep reflection into the record's package.

Common situations: Deploying Avro-generated classes in the user jar with classloader resolution-mode child-first; running on JDK 17+ where illegal reflective access is denied by default; shaded/relocated avro classes where MODEL$ exists under a different runtime context.

Related errors


AI-assisted analysis of apache/flink@2f3c205e92 (2026-08-14). Data as JSON: /api/errors/50e566e88f347c7c. Report an issue: GitHub.