diff --git a/strix/core/execution.py b/strix/core/execution.py index 2d4ad5f7..ff60fee9 100644 --- a/strix/core/execution.py +++ b/strix/core/execution.py @@ -184,7 +184,7 @@ def _is_transient_model_error(exc: BaseException) -> bool: return True code = _model_error_status_code(exc) if code is not None: - return code >= 400 + return code >= 400 and code not in (401, 402, 403) return isinstance(exc, APIError) diff --git a/tests/test_execution_request_log.py b/tests/test_execution_request_log.py index 28c330ff..426c4d47 100644 --- a/tests/test_execution_request_log.py +++ b/tests/test_execution_request_log.py @@ -96,12 +96,8 @@ async def test_blocked_provider_failure_text_carries_request_id( monkeypatch: pytest.MonkeyPatch, ) -> None: coordinator = AgentCoordinator() - # 4xx errors are retried, so the block repeats until retries run out. - streams = [ - _FakeStream(exc=_blocked_error()) for _ in range(execution._MAX_TRANSIENT_MODEL_RETRIES + 1) - ] with pytest.raises(PermissionDeniedError): - await _run(monkeypatch, streams, coordinator) + await _run(monkeypatch, [_FakeStream(exc=_blocked_error())], coordinator) assert coordinator.statuses["root"] == "failed" error = coordinator.errors["root"] assert "Output blocked by policy" in error diff --git a/tests/test_execution_transient_retry.py b/tests/test_execution_transient_retry.py index a9200794..6b2fd336 100644 --- a/tests/test_execution_transient_retry.py +++ b/tests/test_execution_transient_retry.py @@ -84,8 +84,9 @@ def test_client_errors_are_transient() -> None: "bad", response=httpx.Response(400, request=_request()), body=None ) assert execution._is_transient_model_error(bad_request) is True - for status in (401, 402, 403, 404): - assert execution._is_transient_model_error(_status_error(status)) is True + assert execution._is_transient_model_error(_status_error(404)) is True + for status in (401, 402, 403): + assert execution._is_transient_model_error(_status_error(status)) is False assert execution._is_transient_model_error(ValueError("nope")) is False