testcontainers/testcontainers-java · error · IllegalStateException

Unexpected scheme

Error message

Unexpected scheme %s

What it means

DockerClientProviderStrategy.test() validates a DOCKER_HOST by constructing a socket appropriate to its scheme. The code handles 'unix' and 'npipe' (plus tcp/localhost earlier in the switch); any other scheme (e.g. http://, https://, ftp://, or a typo) reaches the default branch and throws this IllegalStateException, failing the strategy's self-test.

Solutions

  1. Fix DOCKER_HOST to a supported scheme: tcp://host:2375, unix:///var/run/docker.sock, or npipe:////./pipe/docker_engine
  2. Unset DOCKER_HOST to let Testcontainers auto-detect the local Docker environment
  3. Verify with `echo $DOCKER_HOST` / `docker context ls` what the client is actually pointed at

Example fix

// before
DOCKER_HOST=http://localhost:2375
// after
DOCKER_HOST=tcp://localhost:2375
# or simply unset DOCKER_HOST and rely on auto-detection
Defensive patterns

Strategy: validation

Validate before calling

String dh = System.getenv("DOCKER_HOST");
if (dh != null) {
    String scheme = java.net.URI.create(dh).getScheme();
    if (scheme == null || !(scheme.equals("tcp") || scheme.equals("unix") || scheme.equals("npipe") || scheme.equals("http") || scheme.equals("https"))) {
        throw new IllegalStateException("Unsupported DOCKER_HOST scheme: " + scheme + " in " + dh);
    }
}

Try / catch

try {
    DockerClientFactory.instance().client();
} catch (IllegalStateException e) {
    if (e.getMessage().startsWith("Unexpected scheme")) {
        // unset or fix DOCKER_HOST and retry
    }
    throw e;
}

Prevention

When it happens

Trigger: DOCKER_HOST set to a value whose URI scheme is not one of tcp://, unix://, npipe:// — for example DOCKER_HOST=http://localhost:2375 or a malformed/misspelled scheme.

Common situations: Users copying docker CLI -H syntax (tcp://) incorrectly or using an http:// URL; DOCKER_HOST inherited from CI with a proxy-style URL; typos like 'unix:/' or quoted values with stray characters.

Understand the failure class

Background: "Invalid URL" errors: why new URL(), URI.parse, and reqwest::Url reject your string — missing scheme, whitespace, and bad path format — this error's family across 39 libraries.

Related errors


AI-assisted analysis of testcontainers/testcontainers-java@8e549514e3 (2026-09-12). Data as JSON: /api/errors/fe930c835842653d. Report an issue: GitHub.

Appendix: source

Thrown at core/src/main/java/org/testcontainers/dockerclient/DockerClientProviderStrategy.java:198

                }
                socketProvider = socketFactory::createSocket;
                socketAddress = new InetSocketAddress(dockerHost.getHost(), dockerHost.getPort());
                break;
            case "unix":
            case "npipe":
                if (!new File(dockerHost.getPath()).exists()) {
                    log.debug("DOCKER_HOST socket file '{}' does not exist", dockerHost.getPath());
                    return false;
                }
                socketProvider =
                    () -> {
                        switch (dockerHost.getScheme()) {
                            case "unix":
                                return UnixSocket.get(dockerHost.getPath());
                            case "npipe":
                                return new NamedPipeSocket(dockerHost.getPath());
                            default:
                                throw new IllegalStateException("Unexpected scheme " + dockerHost.getScheme());
                        }
                    };
                socketAddress = new InetSocketAddress("localhost", 2375);
                break;
            default:
                log.warn("Unknown DOCKER_HOST scheme {}, skipping the strategy test...", dockerHost.getScheme());
                return true;
        }

        try (Socket socket = socketProvider.call()) {
            Awaitility
                .await()
                .atMost(TestcontainersConfiguration.getInstance().getClientPingTimeout(), TimeUnit.SECONDS) // timeout after configured duration
                .pollInterval(Duration.ofMillis(200)) // check state every 200ms
                .pollDelay(Duration.ofSeconds(0)) // start checking immediately
                .untilAsserted(() -> socket.connect(socketAddress));
            return true;
        } catch (Exception e) {

View on GitHub (pinned to 8e549514e3)