diff --git a/agent/turn_recovery.py b/agent/turn_recovery.py index 575fe27f56..6d89c588c6 100644 --- a/agent/turn_recovery.py +++ b/agent/turn_recovery.py @@ -111,19 +111,18 @@ def _try_refresh_nous_paid_entitlement_credentials(agent) -> bool: return False -def _repair_transport_credentials(agent: Any) -> Tuple[bool, bool]: +def _repair_transport_credentials(agent: Any) -> bool: """Strip non-ASCII from ``_client_kwargs["default_headers"]`` and the API key. Non-ASCII in the key makes httpx fail encoding the Authorization header — the usual persistent cause of UnicodeEncodeError that survives message/tool sanitization (#6843, e.g. ʋ instead of v from a bad copy-paste). Entra ID bearer providers are callables - minting ASCII JWTs; skip them (``_strip_non_ascii`` would crash). Returns - ``(headers_sanitized, credential_sanitized)``. + minting ASCII JWTs; skip them (``_strip_non_ascii`` would crash). Returns True when + either the headers or the key were repaired. """ _client_kwargs = getattr(agent, "_client_kwargs", None) _default_headers = _client_kwargs.get("default_headers") if isinstance(_client_kwargs, dict) else None - _headers_sanitized = bool(isinstance(_default_headers, dict) and _sanitize_structure_non_ascii(_default_headers)) - _credential_sanitized = False + _repaired = bool(isinstance(_default_headers, dict) and _sanitize_structure_non_ascii(_default_headers)) _raw_key = getattr(agent, "api_key", None) or "" if isinstance(_raw_key, str) and _raw_key: _clean_key = _strip_non_ascii(_raw_key) @@ -134,13 +133,13 @@ def _repair_transport_credentials(agent: Any) -> Tuple[bool, bool]: # The live client reads its own api_key copy on every request. if getattr(agent, "client", None) is not None and hasattr(agent.client, "api_key"): agent.client.api_key = _clean_key - _credential_sanitized = True + _repaired = True _vlines( agent, "⚠️ API key contained non-ASCII characters (bad copy-paste?) — stripped them. " "If auth fails, re-copy the key from your provider's dashboard.", ) - return _headers_sanitized, _credential_sanitized + return _repaired def _recover_unicode_encode_error( @@ -183,8 +182,7 @@ def _recover_unicode_encode_error( # succeed — return False so the error surfaces through the normal path # instead of burning both sanitization passes on unchanged requests. if not _runtime_uses_ascii_encoding(): - _headers_sanitized, _credential_sanitized = _repair_transport_credentials(agent) - if not (_headers_sanitized or _credential_sanitized): + if not _repair_transport_credentials(agent): return False, active_system_prompt agent._unicode_sanitization_passes += 1 _vlines( @@ -207,7 +205,7 @@ def _recover_unicode_encode_error( active_system_prompt = _sanitized_system _system_sanitized = True - _headers_sanitized, _credential_sanitized = _repair_transport_credentials(agent) + _transport_repaired = _repair_transport_credentials(agent) # Always retry on ASCII codec detection: _force_ascii_payload sanitizes the full # api_kwargs next iteration even when the checks above find nothing. @@ -215,8 +213,7 @@ def _recover_unicode_encode_error( _vlines( agent, "⚠️ System encoding is ASCII — stripped non-ASCII characters from request payload. Retrying..." - if (_messages_sanitized or _system_sanitized - or _headers_sanitized or _credential_sanitized) else + if (_messages_sanitized or _system_sanitized or _transport_repaired) else "⚠️ System encoding is ASCII — enabling full-payload sanitization for retry...", ) return True, active_system_prompt