testcontainers/testcontainers-java · error · ContainerFetchException
Failed to pull image
Error message
Failed to pull image: ${imageName} What it means
Testcontainers wraps any failure while pulling a Docker image into a ContainerFetchException with this message. The underlying cause is the last exception seen while streaming the docker pull (e.g. registry auth failures, image not found, network problems); the full docker pull output is logged at error level right before throwing.
Solutions
- Read the logged `docker pull <image>` output above the exception for the real cause (auth, not found, rate limit)
- Verify the image name and tag exist: run `docker pull <imageName>` manually
- Add registry credentials (DockerConfig or TestcontainersConfiguration with registry auth) for private registries
- docker login to the registry and/or configure a registry mirror
- Check network/proxy/VPN connectivity to the registry; retry if it was a transient failure
Example fix
// before
new GenericContainer("mycompany/private-app:latest"); // ContainerFetchException
// after
new GenericContainer("mycompany/private-app:1.4.2") // correct existing tag
// plus credentials configured via docker login / TestcontainersConfiguration Defensive patterns
Strategy: retry
Validate before calling
// shell precheck before running the test
boolean ok = new ProcessBuilder("docker","pull",imageName).inheritIO().start().waitFor() == 0;
if (!ok) throw new IllegalStateException("Image not pullable: " + imageName); Type guard
null
Try / catch
try {
image.resolve(client);
} catch (ContainerFetchException e) {
// inspect e.getCause(); check registry auth / rate limit before retrying
logger.error("Image pull failed for {}: {}", imageName, e.getCause().getMessage());
throw e;
} Prevention
- Always run `docker pull <image>` manually once before adding it to tests
- Pin specific tags, never :latest, to avoid surprise removals
- Authenticate to Docker Hub / private registries to avoid 429 rate limits
- Configure registry mirrors for CI environments
- Log the ContainerFetchException cause, not just the message
When it happens
Trigger: Calling RemoteDockerImage.resolve() (directly or via GenericContainer with an image that is not local) and the pullImageCmd/executable pull fails for all retry attempts — e.g. nonexistent tag, private registry without credentials, or no network/registry access.
Common situations: Typo in image name or tag (e.g. redis:latestr); pulling from Docker Hub rate limits (429 ToomanyRequests) without auth; corporate proxy blocking registry access; private registry missing docker-registry credentials in ~/.docker/config.json or Testcontainers config.
Understand the failure class
Background: "API error: {status}" and "HTTP 401/403/404/429/5xx" errors: non-2xx HTTP responses explained — this error's family across 27 libraries.
Related errors
- Could not connect to Ryuk at
- Elasticsearch container has no network configuration
- Elasticsearch container is not connected to the expected…
- Failed to connect Elasticsearch container to ad-hoc shared…
- Cannot obtain endpoint URL
AI-assisted analysis of testcontainers/testcontainers-java@8e549514e3 (2026-09-12).
Data as JSON: /api/errors/1112201e1297a3e6.
Report an issue: GitHub.
Appendix: source
Thrown at core/src/main/java/org/testcontainers/images/RemoteDockerImage.java:122
Awaitility
.await()
.pollInSameThread()
.pollDelay(Duration.ZERO) // start checking immediately
.atMost(PULL_RETRY_TIME_LIMIT)
.pollInterval(interval)
.until(
tryImagePullCommand(pullImageCmd, logger, dockerImageName, imageName, lastFailure, lastRetryAllowed)
);
if (dockerImageName.get() == null) {
final Exception lastException = lastFailure.get();
logger.error(
"Failed to pull image: {}. Please check output of `docker pull {}`",
imageName,
imageName,
lastException
);
throw new ContainerFetchException("Failed to pull image: " + imageName, lastException);
}
logger.info("Image {} pull took {}", dockerImageName.get(), Duration.between(startedAt, Instant.now()));
LocalImagesCache.INSTANCE.refreshCache(imageName);
return dockerImageName.get();
} catch (DockerClientException e) {
throw new ContainerFetchException("Failed to get Docker client for " + imageName, e);
}
}
private Callable<Boolean> tryImagePullCommand(
PullImageCmd pullImageCmd,
Logger logger,
AtomicReference<String> dockerImageName,
DockerImageName imageName,
AtomicReference<Exception> lastFailure,
Instant lastRetryAllowed
) {View on GitHub (pinned to 8e549514e3)