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).)

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.

Appendix: 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)