testcontainers/testcontainers-java · error · java.lang.IllegalArgumentException
Configured Image Substitutor could not be loaded
Error message
Configured Image Substitutor could not be loaded: ${configuredClassName} What it means
ImageNameSubstitutor.getImageNameSubstitutor loads a custom substitutor class named by testcontainers.image.name.substitutor in ~/.testcontainers.properties. If the class cannot be loaded, instantiated via a no-arg constructor, or cast to ImageNameSubstitutor, it throws IllegalArgumentException including the configured class name. This is a config/classpath problem surfaced at first image-name resolution.
Solutions
- Verify the class name in ~/.testcontainers.properties matches a class on the test classpath, spelled correctly.
- Ensure the class extends ImageNameSubstitutor and has a public no-arg constructor.
- Remove the testcontainers.image.name.substitutor property if you don't need a custom substitutor.
- Check the cause chain in the stack trace for the underlying load/instantiation failure (ClassNotFound vs Instantiation vs constructor exception).
Example fix
// before (testcontainers.properties) testcontainers.image.name.substitutor=com.example.OldSubstitutor // after testcontainers.image.name.substitutor=com.example.MyImageNameSubstitutor // existing, public, no-arg ctor
Defensive patterns
Strategy: validation
Validate before calling
String cls = TestcontainersConfiguration.getInstance().getImageSubstitutorClassName();
if (cls != null) {
Class<?> c = Class.forName(cls);
if (!ImageNameSubstitutor.class.isAssignableFrom(c)) throw new IllegalStateException(cls + " must extend ImageNameSubstitutor");
c.getDeclaredConstructor().setAccessible(true);
} Try / catch
try {
ImageNameSubstitutor sub = ImageNameSubstitutor.instance();
} catch (IllegalArgumentException e) {
if (e.getMessage().startsWith("Configured Image Substitutor could not be loaded")) {
log.error("Check testcontainers.image.name.substitutor: {}", e.getMessage());
}
throw e;
} Prevention
- Keep substitutor classes on the test classpath of every module using the property
- Always provide a public no-arg constructor
- Keep property value and class FQN in sync (avoid renames)
- Add a smoke test that instantiates the configured substitutor
When it happens
Trigger: Setting testcontainers.image.name.substitutor=fully.qualified.ClassName where the class is not on the test classpath, lacks a public no-arg constructor, fails in its constructor/static init, or doesn't extend ImageNameSubstitutor.
Common situations: Typo in the fully-qualified class name; class moved/renamed after a refactor; config set locally but class lives only in another module; dependency not declared in the test classpath; constructor doing DI-dependent work.
Related errors
- Can't instantiate a strategy from
- Services named do not exist, but wait conditions have been…
- Could not get a port for
- Changing startup timeout is not supported with mode
- Unexpected scheme
AI-assisted analysis of testcontainers/testcontainers-java@8e549514e3 (2026-09-12).
Data as JSON: /api/errors/dba4ee57e41ff70c.
Report an issue: GitHub.
Appendix: source
Thrown at core/src/main/java/org/testcontainers/utility/ImageNameSubstitutor.java:68
);
} else {
instance = wrapWithLogging(defaultImplementation);
}
log.info("Image name substitution will be performed by: {}", instance.getDescription());
}
return instance;
}
private static ImageNameSubstitutor getImageNameSubstitutor(ClassLoader classLoader) {
final String configuredClassName = TestcontainersConfiguration.getInstance().getImageSubstitutorClassName();
if (configuredClassName != null) {
try {
return (ImageNameSubstitutor) classLoader.loadClass(configuredClassName).getConstructor().newInstance();
} catch (Exception e) {
throw new IllegalArgumentException(
"Configured Image Substitutor could not be loaded: " + configuredClassName,
e
);
}
}
return StreamSupport
.stream(ServiceLoader.load(ImageNameSubstitutor.class, classLoader).spliterator(), false)
.findFirst()
.orElse(null);
}
public static ImageNameSubstitutor noop() {
return new NoopImageNameSubstitutor();
}
private static ImageNameSubstitutor wrapWithLogging(final ImageNameSubstitutor wrappedInstance) {
return new LogWrappedImageNameSubstitutor(wrappedInstance);View on GitHub (pinned to 8e549514e3)