Troubleshooting

edit

The exceptions

try
{
    await container.StartAsync();
}
catch (ContainerRuntimeException ex)
{
    // The runtime refused an operation: a CLI command (Command, ExitCode, StandardError) or a Docker Engine API request (StatusCode)
    Console.WriteLine($"{ex.Runtime} failed: {ex.Command} {ex.StatusCode} {ex.StandardError}");
    throw;
}
catch (TimeoutException ex)
{
    // Not ready within StartupTimeout: the message names the wait strategy, the state of the container and its last log lines
    Console.WriteLine(ex.Message);
    throw;
}
ExceptionThrown when
ContainerRuntimeExceptionThe runtime refused an operation. Runtime is the runtime; Command, ExitCode, StandardOutput and StandardError describe the command line that failed; StatusCode the request the Docker Engine API refused.
TimeoutExceptionThe wait strategies did not complete within StartupTimeout. The message names the strategy that did not complete, the state of the container and its last log lines.
InvalidOperationExceptionNo runtime is available; the container exited while a log message strategy waited (the message gives its exit code and last log lines); a ReuseId designates a container created from another definition; or the container was used before StartAsync (Id, GetMappedPort, GetConnectionString).
NotSupportedExceptionThe runtime cannot do the operation: pausing with Apple's container, volumes with wslc, a reaper with either.

No container runtime is available

StartAsync throws "No supported container runtime ... is available":

  • Check that the runtime is installed and that its command line is on the PATH of the test process. A test runner started by an IDE does not always have the PATH of your terminal.
  • Check that its daemon runs: start Docker Desktop or the Podman machine, or run container system start for Apple's container.
  • When you chose a runtime driven through its command line (docker, podman, container, wslc), the message "The '...' runtime is not available" ends with the command that checks the daemon and what it printed, or says that it timed out. wslc boots its virtual machine on demand, so the first command can fail or hang for a while after the machine was idle.
  • With the Docker Engine API runtime, read the end of the message: it lists the endpoints the runtime tried and why each one failed. Check DOCKER_HOST, DOCKER_CONTEXT and the current docker context (docker context ls). See Connect to a Docker daemon.

The container is never ready

The TimeoutException says which strategy did not complete and shows the last log lines:

  • The log message changed. A new version of the image may print another message: pin the image tag, and update the strategy along with it.
  • The message is printed twice. Some images start the service twice during their first start; count the occurrences or add Wait.ForPort. See Wait strategies.
  • The container exited. Look at the last log lines: a missing environment variable or a wrong command line usually shows there.
  • The machine is slow. Pulling is not part of the timeout, but the start of a large service on a loaded build agent can be: raise StartupTimeout.
  • The image does not run on the machine. An image published for x64 only, such as SQL Server, does not run on an arm64 machine unless the runtime emulates x64.

A port cannot be reached

  • Use the host port: GetMappedPort(containerPort) returns it. The container port is only reachable from the network of the container.
  • Connect to the address GetHostAddress(containerPort) returns: 127.0.0.1 by default, the host of the daemon when it runs on another machine. See Ports.
  • With a daemon on another machine, publish the port on 0.0.0.0: a port published on the loopback address is only reachable from the daemon's machine.
  • Read the port again after StartAsync or RestartAsync.

Containers are left behind

  • A test run that is killed does not dispose its containers: call CleanupAsync when the tests start, and start a reaper. See Cleaning up.
  • A container that has a ReuseId is kept on purpose. See Reusing a container.
  • With wslc, the images built from a Dockerfile and the anonymous volumes are not removed.

See what the container does