From 6cfeff74d6076a8eff33ca5fe0ccc265c29f7ef1 Mon Sep 17 00:00:00 2001 From: jenkins Date: Sun, 13 Sep 2026 18:37:56 -0500 Subject: [PATCH] hermes: classify mediator conflicts as publication rejections --- services/hermes/scripts/execution_pool_worker.py | 15 ++++++++++++--- .../tests/test_hermes_execution_pool_worker_v2.py | 12 +++++++++++- 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/services/hermes/scripts/execution_pool_worker.py b/services/hermes/scripts/execution_pool_worker.py index 30d08ccd..ca6f8bbc 100644 --- a/services/hermes/scripts/execution_pool_worker.py +++ b/services/hermes/scripts/execution_pool_worker.py @@ -36,8 +36,17 @@ def _post(url: str, value: dict[str, Any], timeout: int = 60) -> dict[str, Any]: url, data=canonical_json(value), method="POST", headers={"Content-Type": "application/json", "Cache-Control": "no-store"}, ) - with urllib.request.urlopen(request, timeout=timeout) as response: - body = response.read(64 * 1024 + 1) + try: + with urllib.request.urlopen(request, timeout=timeout) as response: + body = response.read(64 * 1024 + 1) + except urllib.error.HTTPError as error: + if error.code == 409: + try: + error.read(64 * 1024 + 1) + except (AttributeError, OSError): + pass + raise ProtocolError("local mediator rejected request") from error + raise if len(body) > 64 * 1024: raise ProtocolError("server response exceeds the wire limit") document = json.loads(body) @@ -47,7 +56,7 @@ def _post(url: str, value: dict[str, Any], timeout: int = 60) -> dict[str, Any]: def _client(operation: str, **values: Any) -> dict[str, Any]: response = _post(f"{CLIENT}/v1/client", {"operation": operation, **values}) if response.get("error"): - raise ProtocolError(str(response["error"])) + raise ProtocolError("local mediator rejected request") return response def _poll() -> dict[str, Any] | None: assignment = _client("poll").get("assignment") diff --git a/testing/tests/test_hermes_execution_pool_worker_v2.py b/testing/tests/test_hermes_execution_pool_worker_v2.py index dbec00b6..76306ff8 100644 --- a/testing/tests/test_hermes_execution_pool_worker_v2.py +++ b/testing/tests/test_hermes_execution_pool_worker_v2.py @@ -2,6 +2,7 @@ from __future__ import annotations +import io import json import os import subprocess @@ -63,9 +64,18 @@ def test_post_client_and_poll_validate_every_response_boundary(monkeypatch): ) with pytest.raises(protocol.ProtocolError, match="object"): worker._post("http://mediator", {}) + conflict = urllib.error.HTTPError( + "http://mediator", 409, "Conflict", {}, io.BytesIO(b'{"error":"untrusted detail"}') + ) + monkeypatch.setattr( + worker.urllib.request, "urlopen", lambda *_a, **_k: (_ for _ in ()).throw(conflict) + ) + with pytest.raises(protocol.ProtocolError, match="local mediator rejected") as rejected: + worker._post("http://mediator", {}) + assert "untrusted detail" not in str(rejected.value) monkeypatch.setattr(worker, "_post", lambda *_a, **_k: {"error": "denied"}) - with pytest.raises(protocol.ProtocolError, match="denied"): + with pytest.raises(protocol.ProtocolError, match="local mediator rejected"): worker._client("poll") monkeypatch.setattr(worker, "_post", lambda *_a, **_k: {"safe": True}) assert worker._client("poll") == {"safe": True}