Skip to content

Commit b0274f3

Browse files
authored
Handle Docker exceptions without explanation attribute (#12570)
## Summary - add a helper to extract readable messages from Docker exceptions without relying on `explanation` - reuse the helper when wrapping Docker errors so BuildAppError always gets a message and fix a typo in exception handling ## Fixes https://read-the-docs.sentry.io/issues/4883886678/ https://read-the-docs.sentry.io/issues/5030265593/ ------ [Codex Task](https://chatgpt.com/codex/tasks/task_e_69148da42d508320a9d619e573596b09)
1 parent cc361ed commit b0274f3

File tree

1 file changed

+21
-3
lines changed

1 file changed

+21
-3
lines changed

readthedocs/doc_builder/environments.py

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -599,6 +599,21 @@ class DockerBuildEnvironment(BaseBuildEnvironment):
599599
command_class = DockerBuildCommand
600600
container_image = DOCKER_IMAGE
601601

602+
@staticmethod
603+
def _get_docker_exception_message(exc):
604+
"""Return a human readable message from a Docker exception."""
605+
606+
# ``docker.errors.DockerException`` usually exposes ``explanation`` but
607+
# some subclasses created when wrapping other libraries (``requests``,
608+
# ``urllib3``) do not. Accessing it blindly raises ``AttributeError``.
609+
# Fallback to ``str(exc)`` so we always have a useful message.
610+
message = getattr(exc, "explanation", None)
611+
if not message:
612+
message = str(exc)
613+
if not message:
614+
message = repr(exc)
615+
return message
616+
602617
def __init__(self, *args, **kwargs):
603618
container_image = kwargs.pop("container_image", None)
604619
super().__init__(*args, **kwargs)
@@ -663,7 +678,8 @@ def __enter__(self):
663678
client.remove_container(self.container_id)
664679
except (DockerAPIError, ConnectionError) as exc:
665680
raise BuildAppError(
666-
BuildAppError.GENERIC_WITH_BUILD_ID, exception_message=exc.explanation
681+
BuildAppError.GENERIC_WITH_BUILD_ID,
682+
exception_message=self._get_docker_exception_message(exc),
667683
) from exc
668684

669685
# Create the checkout path if it doesn't exist to avoid Docker creation
@@ -739,7 +755,8 @@ def get_client(self):
739755
return self.client
740756
except DockerException as exc:
741757
raise BuildAppError(
742-
BuildAppError.GENERIC_WITH_BUILD_ID, exception_message=exc.explanation
758+
BuildAppError.GENERIC_WITH_BUILD_ID,
759+
exception_message=self._get_docker_exception_message(exc),
743760
) from exc
744761

745762
def _get_binds(self):
@@ -874,7 +891,8 @@ def create_container(self):
874891

875892
except (DockerAPIError, ConnectionError) as exc:
876893
raise BuildAppError(
877-
BuildAppError.GENERIC_WITH_BUILD_ID, exception_messag=exc.explanation
894+
BuildAppError.GENERIC_WITH_BUILD_ID,
895+
exception_message=self._get_docker_exception_message(exc),
878896
) from exc
879897

880898
def _run_background_healthcheck(self):

0 commit comments

Comments
 (0)