apache/maven · error · ModelInterpolationException

Failed to interpolate field: " + field + " on class: " + cls

Error message

Failed to interpolate field: " + field + " on class: " + cls.getName()

What it means

Thrown by StringSearchModelInterpolator while reflectively traversing model fields: field.get(target) threw IllegalArgumentException or IllegalAccessException. This interpolator walks the object graph (including arrays and superclass fields) to interpolate strings in place; the exception names the exact field and class where reflection failed. Causes include a mutated/incompatible field type (the field was changed between the accessibility check and the read) or access being denied after setAccessible in a hardened JVM.

Source

Thrown at compat/maven-compat/src/main/java/org/apache/maven/project/interpolation/StringSearchModelInterpolator.java:252

                                                    } else {
                                                        interpolationTargets.add(value);
                                                    }
                                                }
                                            }
                                        }
                                    }
                                } else {
                                    Object value = field.get(target);
                                    if (value != null) {
                                        if (field.getType().isArray()) {
                                            evaluateArray(value);
                                        } else {
                                            interpolationTargets.add(value);
                                        }
                                    }
                                }
                            } catch (IllegalArgumentException | IllegalAccessException e) {
                                throw new ModelInterpolationException(
                                        "Failed to interpolate field: " + field + " on class: " + cls.getName(), e);
                            }
                        } finally {
                            field.setAccessible(isAccessible);
                        }
                    }
                }

                traverseObjectWithParents(cls.getSuperclass(), target);
            }
        }

        private boolean isQualifiedForInterpolation(Class<?> cls) {
            return !cls.getPackage().getName().startsWith("java")
                    && !cls.getPackage().getName().startsWith("sun.nio.fs")
                    // org.apache.maven.api.model.InputLocation can be self-referencing
                    && !cls.getName().equals("org.apache.maven.api.model.InputLocation");
        }

View on GitHub (pinned to e4093d4e12)

Solutions

  1. Check the named field/class: it pinpoints exactly which model part failed; reproduce with a single-threaded build (-T1) to rule out concurrent model mutation
  2. Remove/upgrade plugins that swap model field values in background threads
  3. Prefer the standard project builder path (field interpolation) over manually invoking StringSearchModelInterpolator on shared models
  4. On restrictive JDKs, ensure legacy reflection is permitted (no deny-ish --add-opens removal) or use a Maven version compatible with that JDK
  5. Clone the model before interpolating when embedding, so no other code mutates it mid-traversal

Example fix

// before: interpolating a shared model while another thread mutates it
interpolator.interpolate(sharedModel, ...);
// after: interpolate a private copy
Model copy = sharedModel.clone();
interpolator.interpolate(copy, ...);
Defensive patterns

Strategy: try-catch

Validate before calling

// When embedding: interpolate a clone so concurrent mutation cannot break reflection
Model snapshot = model.clone();

Try / catch

try {
    interpolator.interpolate(model, ...);
} catch (ModelInterpolationException e) {
    if (e.getMessage().startsWith("Failed to interpolate field")) {
        // named field/class pinpoints the mutation; retry single-threaded on a clone
    }
}

Prevention

When it happens

Trigger: traverseObjectWithParents() calls Field.get on a model class when (a) the object held in the field's value is not an instance of the field's declared type (IllegalArgumentException, usually concurrent model mutation by another thread/plugin), or (b) setAccessible(false)-style denial by a SecurityManager/JPMS strong encapsulation (IllegalAccessException).

Common situations: Parallel builds where one plugin mutates the model while interpolation runs; custom model classes with covariance tricks; running Maven on JDKs with module access restrictions hitting legacy reflection; corrupted plugin-provided model instances.

Related errors


AI-assisted analysis of apache/maven@e4093d4e12 (2026-08-21). Data as JSON: /api/errors/cb39c04aad9c1608. Report an issue: GitHub.