Skip to content

Bug: file descriptor leak in HttpWaitStrategy #1115

Description

@loic-simon

Describe the bug

When using an HttpWaitStrategy in DockerContainer.waiting_for and the container image respond with an error code, the underlying HttpError is not correctly handled, leaking a file descriptor.

To Reproduce

Example with the https://hub.docker.com/r/typesense/typesense image, but should work with any image providing a HTTP server that takes a few seconds to start:

# mre.py
from testcontainers.core.container import DockerContainer
from testcontainers.core.wait_strategies import HttpWaitStrategy

print("starting")
with (
    DockerContainer("typesense/typesense:30.2", command="--data-dir /home --api-key=whatever --enable-cors")
    .with_exposed_ports(8108)
    .waiting_for(HttpWaitStrategy(8108, "/health").for_status_code(200))
):
    print("ok")
$ python -Werror::ResourceWarning mre.py
starting
Exception ignored while calling deallocator <function _TemporaryFileCloser.__del__ at 0x101984880>:
Traceback (most recent call last):
  File "/Users/loic/.local/share/uv/python/cpython-3.14.4-macos-aarch64-none/lib/python3.14/tempfile.py", line 484, in __del__
    _warnings.warn(self.warn_message, ResourceWarning)
ResourceWarning: Implicitly cleaning up <HTTPError 503: ''>
ok

Root Cause / fix

The issue is in

def _handle_http_error(self, error: Union[URLError, HTTPError]) -> bool:
"""Handle HTTP errors and return True if error is acceptable."""
if isinstance(error, HTTPError) and (
error.code in self._status_codes
or (self._status_code_predicate and self._status_code_predicate(error.code))
):
return True
logger.debug(f"HTTP request failed: {error!s}")
return False

HttpError holds a reference to a (temporary) file storing the HTTP response, so it must be handled in a context manager to safely clean the file when the error is discarded.

Fixed MRE:

# mre.py
from contextlib import nullcontext
from typing import Union
from urllib.error import HTTPError, URLError

from testcontainers.core.container import DockerContainer
from testcontainers.core.wait_strategies import HttpWaitStrategy

class _FixedHttpWaitStrategy(HttpWaitStrategy):
    def _handle_http_error(self, error: Union[URLError, HTTPError]) -> bool:
        with error if isinstance(error, HTTPError) else nullcontext(error):
            return super()._handle_http_error(error)

print("starting")
with (
    DockerContainer("typesense/typesense:30.2", command="--data-dir /home --api-key=whatever --enable-cors")
    .with_exposed_ports(8108)
    .waiting_for(_FixedHttpWaitStrategy(8108, "/health").for_status_code(200))
):
    print("ok")

Runtime environment

  • MacOS 26.6.2
  • Python 3.14.4
  • testcontainers 4.14.2

I can work on a PR if you'd like!

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions