Skip to content

Raise PortAlreadyInUse when Docker reports port is already allocated - #9347

Open
hippi345 wants to merge 3 commits into
aws:developfrom
hippi345:fix/port-already-allocated-9295
Open

hippi345 wants to merge 3 commits into
aws:developfrom
hippi345:fix/port-already-allocated-9295

Conversation

@hippi345

Copy link
Copy Markdown

Summary

This PR fixes issue #9295 by detecting the "port is already allocated" error during container creation and raising the appropriate PortAlreadyInUse exception instead of the generic DockerContainerCreationFailedException.

Problem

When container engines like Finch bind host ports during containers.create() (rather than during start() like Docker), a port conflict results in a misleading error message:

Container creation failed: failed to parse port mapping "0.0.0.0:7311:8080/tcp": bind for 0.0.0.0:7311 failed: port is already allocated, check template for potential issue

The error incorrectly directs users to check their template, when the actual issue is that the host port is already in use.

Solution

Modified Container.create() in samcli/local/docker/container.py to:

  1. Check if the Docker APIError message contains "port is already allocated"
  2. If so, raise PortAlreadyInUse (the same exception already used in the start() method for Docker)
  3. Otherwise, maintain the existing behavior with DockerContainerCreationFailedException

This provides users with accurate error information regardless of which container engine they use or when the engine performs port binding.

Testing

  • Added unit test test_create_raises_port_already_in_use_when_port_allocated that mocks a Docker APIError with "port is already allocated" message
  • Verified the test fails without the fix (raises DockerContainerCreationFailedException)
  • Verified the test passes with the fix (raises PortAlreadyInUse)
  • All 85 existing unit tests in test_container.py continue to pass

Fixes #9295


Target branch: develop

Detect Docker APIError containing 'port is already allocated' during
container creation and raise PortAlreadyInUse instead of the generic
DockerContainerCreationFailedException. This provides users with
accurate error information when a host port is already in use,
particularly on container engines like Finch that bind ports during
create() rather than start().

Add unit test to verify PortAlreadyInUse is raised when the Docker API
returns a port allocation error during container creation.

Fixes aws#9295
@github-actions github-actions Bot added area/local/start-api sam local start-api command area/local/invoke sam local invoke command area/local/start-invoke pr/external stage/needs-triage Automatically applied to new issues and PRs, indicating they haven't been looked at. labels Oct 15, 2026
@hippi345
hippi345 marked this pull request as ready for review October 15, 2026 15:34
@hippi345
hippi345 requested a review from a team as a code owner October 15, 2026 15:34
Comment thread samcli/local/docker/container.py Outdated
Comment on lines +292 to +293
if "port is already allocated" in str(ex):
raise PortAlreadyInUse(str(ex)) from ex

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

According to the issue #9347 (in expected result section), changing the error class to PortAlreadyInUse will mess up downstream error catching. The suggestion was simply to change the message.

@reedham-aws reedham-aws added area/finch and removed stage/needs-triage Automatically applied to new issues and PRs, indicating they haven't been looked at. labels Oct 16, 2026
@aws-sam-tooling-bot

aws-sam-tooling-bot Bot commented Oct 26, 2026 •

Copy link
Copy Markdown

🧪 Hands-on test plan (not run: external contributor)

Commit ae244b7. A maintainer can run these checks; nothing here has been tested.

Summary: Builds realistic docker-py APIError objects (HTTP 500 with a JSON 'message' body, as the engine API returns) and passes them to Container.create() through a mocked client. For a Finch-style 'port is already allocated' error, the message should explain the port conflict, drop the 'check template' hint and chain the original error. Other create errors should keep the old message. Also runs tests/unit/local/docker/test_container.py.

For a maintainer to check:

  • PR description does not match the diff: the description says create() now raises PortAlreadyInUse instead of DockerContainerCreationFailedException, but samcli/local/docker/container.py still raises ContainerCreationFailedException, with a new port-conflict message and 'from ex' chaining. The added…

Planned checks (4):

  1. Main path from issue #‍9295: the engine reports 'port is already allocated' during containers.create() (Finch behavior). The message should point at t…
  2. Regression check: a create error unrelated to ports must keep the existing message and behavior.
  3. Scope of the string match: Docker Desktop's wording 'Ports are not available' (the phrase start() checks for) does not trigger the new create() branc…
  4. Run the module's unit tests, including the new test_create_reports_port_in_use_when_port_allocated, to confirm the change and the existing create/sta…
Commands
python -m venv .venv
. .venv/bin/activate && SAM_CLI_DEV=1 pip install -e '.[dev]'
mkdir -p /tmp/handson && cat > /tmp/handson/harness.py <<'EOF'
import sys
from unittest.mock import MagicMock
import requests
from docker.errors import create_api_error_from_http_exception
from samcli.local.docker.container import Container, ContainerContext
from samcli.local.docker.exceptions import ContainerCreationFailedException, PortAlreadyInUse

msg = sys.argv[1]
resp = requests.Response()
resp.status_code = 500
resp._content = ('{"message": %s}' % __import__('json').dumps(msg)).encode()
resp.headers['Content-Type'] = 'application/json'
resp.url = 'http+docker://localhost/v1.41/containers/create'
http_err = requests.exceptions.HTTPError(response=resp)
try:
    create_api_error_from_http_exception(http_err)
except Exception as e:
    api_err = e

client = MagicMock()
client.containers.create.side_effect = api_err
c = Container('public.ecr.aws/lambda/python:3.12', 'handler', '/var/task', '/tmp/handson', docker_client=client)
try:
    c.create(ContainerContext.INVOKE)
    print('NO_EXCEPTION')
except Exception as ex:
    print('CLASS=' + type(ex).__name__)
    print('IS_CCFE=' + str(isinstance(ex, ContainerCreationFailedException)))
    print('IS_PORT_IN_USE=' + str(isinstance(ex, PortAlreadyInUse)))
    print('CAUSE_IS_APIERROR=' + str(ex.__cause__ is api_err))
    print('MESSAGE=' + str(ex))
EOF
# S1
. .venv/bin/activate && python /tmp/handson/harness.py 'failed to parse port mapping "0.0.0.0:7311:8080/tcp": bind for 0.0.0.0:7311 failed: port is already allocated'
# S2
. .venv/bin/activate && python /tmp/handson/harness.py 'invalid mount config for type "bind": bind source path does not exist: /nope'
# S3
. .venv/bin/activate && python /tmp/handson/harness.py 'Ports are not available: exposing port TCP 127.0.0.1:5858 -> 0.0.0.0:0: listen tcp 127.0.0.1:5858: bind: address already in use'
# S4
. .venv/bin/activate && python -m pytest tests/unit/local/docker/test_container.py -q -p no:cacheprovider

🤖 Automated hands-on test — not a review approval · model claude-opus-5.5 · commit ae244b7

History (2)
Commit Result When (UTC) Build
ae244b7 Plan only (not run) 2026-10-29 20:08
abd061e Plan only (not run) 2026-10-26 20:12

@hippi345

Copy link
Copy Markdown
Author

good call, I switched it back so it still raises ContainerCreationFailedException and only the message changes, it now says the host port is already in use instead of pointing at the template, so start-api and start-lambda keep catching it the same way and the original docker error is still chained on it

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bug: a taken host port is reported as a template problem on Finch, because Finch fails at container create rather than start

2 participants