Repository navigation
Fail wait strategies as soon as the container exits - #1512
Open
cristianrgreco wants to merge 5 commits into
Open
cristianrgreco wants to merge 5 commits into
cristianrgreco wants to merge 5 commits into
Conversation
Wait.forHttp() polled until the startup timeout after the container had
exited. Exit detection (abortOnContainerExit) is now on by default; pass
{ abortOnContainerExit: false } to keep waiting.
This was referenced Oct 12, 2026
cristianrgreco
marked this pull request as draft
October 13, 2026 15:55
cristianrgreco
marked this pull request as ready for review
October 13, 2026 16:08
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Wait.forHttp()kept polling until the startup timeout after the container had exited, so a service that crashed during startup heldstart()for the whole timeout and failed withURL /health not accessible after 60000ms. The other wait strategies did fail quickly, but only because the Docker call they make fails once the container is gone, so each reported the exit differently, and not always as an exit.Exit detection now lives in
AbstractWaitStrategyand applies to every wait strategy:retryUntil(protected) wraps the polling loop. After an attempt that is not ready, or that throws, it inspects the container and fails withContainer exited with code <code>if the container has exited. Otherwise it keeps polling, or rethrows the attempt's error.withAbortOnContainerExit(false)(public) opts out.Wait.forAll([...]).withAbortOnContainerExit(false)applies it to every child strategy.Wait.forHttp(path, port, { abortOnContainerExit: false })keeps working.Wait.forListeningPorts(),Wait.forLogMessage()andWait.forHealthCheck()now return their concrete classes, so the method can be chained on them.Per strategy, before and after, for a container that exits during the wait:
Container exited with code <code>(HTTP code 409) container stopped/paused - container <id> is not runningContainer exited with code <code>Container exited with code <code>Health check failed: unhealthy(Docker marks a stopped container unhealthy)Container exited with code <code>Log stream ended and message "..." was not receivedContainer exited with code <code>StartupCheckStrategyrestarting, and only reportsexitedonce the policy gives up..insecureTls(); the method is.allowInsecure().Other implementations
checkStateinwait/wait.go).OneShotopt-out (ContainerNotRunningException, feat: Throw if container not running testcontainers-dotnet#1550). It also keys on statusexited.This PR does what .NET does, but in the base class rather than around the strategies, so containers that are expected to exit (one-shot, or "log a message then exit") keep working without an opt-out.
Verification
format,lint,knip,check-compiles: clean.packages/testcontainers/src: 452 tests, 1 failure:should not pull existing image without pull policy("Failed to build image"). It starts no container, and its file passed on its own (10/10).Red-green
Each new test was run on its own against the code before this change, then after it:
should fail when the container exits(HTTP code 409) container stopped/paused ... is not runningshould fail when the container exitsHealth check failed: unhealthyshould fail when the container exits before logging the messageLog stream ended and message "Ready" was not receivedshould fail when the container exits before the wait succeedsURL /hello-world not accessible after 60000ms(60.3s)should apply abortOnContainerExit to its wait strategiesAbstractWaitStrategyunit tests (exit, failed attempt, restarting, opt-out)should wait for a log message from a container that then exitspasses before and after.should keep waiting after the container exits when abortOnContainerExit is false(HTTP) passes before and after.Not breaking
Waitfactories return narrower types.withAbortOnContainerExit(false)restores the previous behaviour.open-prworkflow lists undermajor. It is labelledminorbecause no start that succeeds today changes outcome; testcontainers-dotnet shipped its equivalent default-on check in a minor release (4.8.0).Closes #1507