From 9f6595b45fd8ae5e01e6969d4793d87d462c28e2 Mon Sep 17 00:00:00 2001 From: rahulkini31 Date: Thu, 9 Apr 2026 20:12:59 +0530 Subject: [PATCH] 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 Signed-off-by: rahulkini31 --- podman/domain/containers_run.py | 12 +++++- podman/tests/unit/test_containersmanager.py | 44 +++++++++++++++++++++ 2 files changed, 55 insertions(+), 1 deletion(-) diff --git a/podman/domain/containers_run.py b/podman/domain/containers_run.py index 7ab60f97..fee4d45d 100644 --- a/podman/domain/containers_run.py +++ b/podman/domain/containers_run.py @@ -8,7 +8,7 @@ from podman.domain.containers import Container from podman.domain.images import Image -from podman.errors import ContainerError, ImageNotFound +from podman.errors import APIError, ContainerError, ImageNotFound logger = logging.getLogger("podman.containers") @@ -72,9 +72,19 @@ def run( if isinstance(command, str): command = [command] + _needs_pull = False try: container = self.create(image=image_id, command=command, **kwargs) # type: ignore[attr-defined] except ImageNotFound: + _needs_pull = True + except APIError as e: + # Podman may return HTTP 500 with "image not known" instead of 404 + # when an image is missing locally; re-raise any unrelated API errors. + if "image not known" not in str(e): + raise + _needs_pull = True + + if _needs_pull: self.podman_client.images.pull( # type: ignore[attr-defined] image_id, auth_config=kwargs.get("auth_config"), diff --git a/podman/tests/unit/test_containersmanager.py b/podman/tests/unit/test_containersmanager.py index 92f58896..01083adc 100644 --- a/podman/tests/unit/test_containersmanager.py +++ b/podman/tests/unit/test_containersmanager.py @@ -644,6 +644,50 @@ def test_run(self, mock): self.assertEqual(next(actual), b"This is a unittest - line 1") self.assertEqual(next(actual), b"This is a unittest - line 2") + @requests_mock.Mocker() + def test_run_pulls_image_on_api_error_image_not_known(self, mock): + """run() should pull image and retry when Podman returns 500 'image not known'.""" + image_id = "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + image_id_encoded = image_id.replace(":", "%3A") + container_id = FIRST_CONTAINER["Id"] + + mock.post( + tests.LIBPOD_URL + "/containers/create", + [ + { + "status_code": 500, + "json": {"cause": "image not known", "message": "fedora: image not known"}, + }, + { + "status_code": 201, + "json": {"Id": container_id, "Warnings": []}, + }, + ], + ) + mock.post( + tests.LIBPOD_URL + "/images/pull", + json={"error": "", "id": image_id, "images": [image_id], "stream": ""}, + ) + mock.get( + tests.LIBPOD_URL + f"/images/{image_id_encoded}/json", + json={"Id": image_id}, + ) + mock.post( + tests.LIBPOD_URL + f"/containers/{container_id}/start", + status_code=204, + ) + mock.get( + tests.LIBPOD_URL + f"/containers/{container_id}/json", + json=FIRST_CONTAINER, + ) + + with patch.multiple(Container, logs=DEFAULT, wait=DEFAULT, autospec=True) as mock_container: + mock_container["wait"].return_value = 0 + mock_container["logs"].return_value = iter([b"output"]) + + actual = self.client.containers.run("fedora", "/usr/bin/ls") + self.assertIsInstance(actual, bytes) + @requests_mock.Mocker() def test_create_all_healthcheck_parameters(self, mock): """Test that all healthcheck parameters are correctly passed to the API."""