Skip to content

Commit 9f6595b

Browse files
rahulkini31claude
andcommitted
fix: auto-pull image in run() when Podman returns 500 'image not known'
When an image is not present locally, Podman returns HTTP 500 with "image not known" rather than HTTP 404. This caused run() to raise APIError instead of triggering the existing ImageNotFound catch block, so the auto-pull never happened. Extend the except clause to also catch APIError, re-raising it unless the message indicates a missing image. Add a unit test covering this path. Closes #594 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Signed-off-by: rahulkini31 <rahulkini369@gmail.com>
1 parent 52a3036 commit 9f6595b

2 files changed

Lines changed: 55 additions & 1 deletion

File tree

podman/domain/containers_run.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
from podman.domain.containers import Container
1010
from podman.domain.images import Image
11-
from podman.errors import ContainerError, ImageNotFound
11+
from podman.errors import APIError, ContainerError, ImageNotFound
1212

1313
logger = logging.getLogger("podman.containers")
1414

@@ -72,9 +72,19 @@ def run(
7272
if isinstance(command, str):
7373
command = [command]
7474

75+
_needs_pull = False
7576
try:
7677
container = self.create(image=image_id, command=command, **kwargs) # type: ignore[attr-defined]
7778
except ImageNotFound:
79+
_needs_pull = True
80+
except APIError as e:
81+
# Podman may return HTTP 500 with "image not known" instead of 404
82+
# when an image is missing locally; re-raise any unrelated API errors.
83+
if "image not known" not in str(e):
84+
raise
85+
_needs_pull = True
86+
87+
if _needs_pull:
7888
self.podman_client.images.pull( # type: ignore[attr-defined]
7989
image_id,
8090
auth_config=kwargs.get("auth_config"),

podman/tests/unit/test_containersmanager.py

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -644,6 +644,50 @@ def test_run(self, mock):
644644
self.assertEqual(next(actual), b"This is a unittest - line 1")
645645
self.assertEqual(next(actual), b"This is a unittest - line 2")
646646

647+
@requests_mock.Mocker()
648+
def test_run_pulls_image_on_api_error_image_not_known(self, mock):
649+
"""run() should pull image and retry when Podman returns 500 'image not known'."""
650+
image_id = "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
651+
image_id_encoded = image_id.replace(":", "%3A")
652+
container_id = FIRST_CONTAINER["Id"]
653+
654+
mock.post(
655+
tests.LIBPOD_URL + "/containers/create",
656+
[
657+
{
658+
"status_code": 500,
659+
"json": {"cause": "image not known", "message": "fedora: image not known"},
660+
},
661+
{
662+
"status_code": 201,
663+
"json": {"Id": container_id, "Warnings": []},
664+
},
665+
],
666+
)
667+
mock.post(
668+
tests.LIBPOD_URL + "/images/pull",
669+
json={"error": "", "id": image_id, "images": [image_id], "stream": ""},
670+
)
671+
mock.get(
672+
tests.LIBPOD_URL + f"/images/{image_id_encoded}/json",
673+
json={"Id": image_id},
674+
)
675+
mock.post(
676+
tests.LIBPOD_URL + f"/containers/{container_id}/start",
677+
status_code=204,
678+
)
679+
mock.get(
680+
tests.LIBPOD_URL + f"/containers/{container_id}/json",
681+
json=FIRST_CONTAINER,
682+
)
683+
684+
with patch.multiple(Container, logs=DEFAULT, wait=DEFAULT, autospec=True) as mock_container:
685+
mock_container["wait"].return_value = 0
686+
mock_container["logs"].return_value = iter([b"output"])
687+
688+
actual = self.client.containers.run("fedora", "/usr/bin/ls")
689+
self.assertIsInstance(actual, bytes)
690+
647691
@requests_mock.Mocker()
648692
def test_create_all_healthcheck_parameters(self, mock):
649693
"""Test that all healthcheck parameters are correctly passed to the API."""

0 commit comments

Comments
 (0)