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

  1. Read the logged `docker pull <image>` output above the exception for the real cause (auth, not found, rate limit)
  2. Verify the image name and tag exist: run `docker pull <imageName>` manually
  3. Add registry credentials (DockerConfig or TestcontainersConfiguration with registry auth) for private registries
  4. docker login to the registry and/or configure a registry mirror
  5. 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

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


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)