Skip to content

Commit 8ae0e73

Browse files
fix(hosting): retry standard transient errors; type resolver scopes as list
- Treat TimeoutError and ConnectionError (including ConnectionResetError) as transient before the message and status checks. Bare instances have empty messages, so they were not retried. - Type the ObservabilityTokenResolver scopes parameter as list[str], which is what the cache passes and what the README documents. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 5cbf5f6b-cc40-4b7e-a591-65848db73a12
1 parent 2a7b006 commit 8ae0e73

2 files changed

Lines changed: 20 additions & 1 deletion

File tree

‎libraries/microsoft-agents-a365-observability-hosting/microsoft_agents_a365/observability/hosting/token_cache_helpers/agent_token_cache.py‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121

2222
logger = logging.getLogger(__name__)
2323

24-
ObservabilityTokenResolver = Callable[[str, str, Sequence[str]], str | Awaitable[str | None] | None]
24+
ObservabilityTokenResolver = Callable[[str, str, list[str]], str | Awaitable[str | None] | None]
2525

2626

2727
@dataclass
@@ -271,6 +271,9 @@ def _is_expired(self, entry: _Entry) -> bool:
271271
return True
272272

273273
def _is_retriable_error(self, error: Exception) -> bool:
274+
if isinstance(error, (TimeoutError, ConnectionError)):
275+
return True
276+
274277
message = str(error).lower()
275278
if "timeout" in message or "econnreset" in message or "network" in message:
276279
return True

‎tests/observability/hosting/token_cache_helpers/test_agent_token_cache.py‎

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,22 @@ async def no_sleep(delay: float) -> None:
207207
assert await token_cache.get_observability_token("agent", "tenant") == token
208208

209209

210+
@pytest.mark.asyncio
211+
@pytest.mark.parametrize("error", [TimeoutError(), ConnectionResetError()])
212+
async def test_refresh_retries_standard_transient_exceptions(token_cache, monkeypatch, error):
213+
"""Bare timeout and connection-reset errors are retried even without a message."""
214+
token = make_jwt(300)
215+
resolver = MagicMock(side_effect=[error, token])
216+
217+
async def no_sleep(delay: float) -> None:
218+
return None
219+
220+
monkeypatch.setattr(asyncio, "sleep", no_sleep)
221+
222+
assert await token_cache.refresh_observability_token("agent", "tenant", resolver) == token
223+
assert resolver.call_count == 2
224+
225+
210226
@pytest.mark.asyncio
211227
async def test_refresh_deduplicates_concurrent_same_identity_acquisition(token_cache):
212228
"""Concurrent refreshes for the same key share the first acquired token."""

0 commit comments

Comments
 (0)