From 0699540e80500cd0dffb9080a098035e94b329f7 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Fri, 29 May 2026 22:06:14 +0000 Subject: [PATCH] fix(oauth): improve refresh-token error message; remove 'please' and dead placeholder The CDK's DeclarativeOauth2Authenticator surfaced a refresh-token error as 'Refresh token is invalid or expired. Please re-authenticate from Sources//Settings.' That message violates the writing-good-error-messages guidelines on multiple counts: - Remediation embedded in the user-facing string ('Please re-authenticate ...') - 'Please' emotional/blame language - Literal '' placeholder that never gets substituted - Over-claims 'invalid or expired' when the 4xx may actually be a malformed refresh request (e.g. Gong's generate-customer-token endpoint, tracked in airbytehq/airbyte-internal-issues#16467) The new message states only the observed condition without over-claiming the cause or instructing the user to take an action that may not resolve the failure. The internal_message now also captures the HTTP status code so developers debugging logs can see the upstream response status. Resolves https://github.com/airbytehq/airbyte-internal-issues/issues/16468 Co-Authored-By: bot_apk --- .../http/requests_native_auth/abstract_oauth.py | 10 ++++++++-- .../requests_native_auth/test_requests_native_auth.py | 8 +++++--- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/airbyte_cdk/sources/streams/http/requests_native_auth/abstract_oauth.py b/airbyte_cdk/sources/streams/http/requests_native_auth/abstract_oauth.py index 111583a63..b0c33e8a6 100644 --- a/airbyte_cdk/sources/streams/http/requests_native_auth/abstract_oauth.py +++ b/airbyte_cdk/sources/streams/http/requests_native_auth/abstract_oauth.py @@ -305,9 +305,15 @@ def _make_handled_request(self) -> Any: failure_type=FailureType.transient_error, ) if self._wrap_refresh_token_exception(e): - message = "Refresh token is invalid or expired. Please re-authenticate from Sources//Settings." + status_code = e.response.status_code if e.response is not None else "unknown" raise AirbyteTracedException( - internal_message=message, message=message, failure_type=FailureType.config_error + message="OAuth token refresh failed with a refresh-token error response.", + internal_message=( + f"OAuth token refresh failed with HTTP {status_code}; " + "response matched configured refresh-token error indicators " + "(see DEBUG-level token refresh log for full response)." + ), + failure_type=FailureType.config_error, ) raise except Exception as e: diff --git a/unit_tests/sources/streams/http/requests_native_auth/test_requests_native_auth.py b/unit_tests/sources/streams/http/requests_native_auth/test_requests_native_auth.py index 71183b5aa..9d762eb1d 100644 --- a/unit_tests/sources/streams/http/requests_native_auth/test_requests_native_auth.py +++ b/unit_tests/sources/streams/http/requests_native_auth/test_requests_native_auth.py @@ -575,9 +575,11 @@ def test_refresh_access_token_wrapped( oauth.refresh_access_token() if wrapped: - error_message = "Refresh token is invalid or expired. Please re-authenticate from Sources//Settings." - assert exc_info.value.internal_message == error_message - assert exc_info.value.message == error_message + expected_message = "OAuth token refresh failed with a refresh-token error response." + assert exc_info.value.message == expected_message + assert exc_info.value.internal_message.startswith( + f"OAuth token refresh failed with HTTP {response_code}" + ) assert exc_info.value.failure_type == FailureType.config_error