-
Notifications
You must be signed in to change notification settings - Fork 6k
fix(gcp): use refreshed gcloud token after reauth (retry Vertex AI on 401/403) #9849
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
24489e6
bdd7638
3a12606
67f139a
d47207d
9e52335
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -284,6 +284,7 @@ impl GcpVertexAIProvider { | |
| let mut overloaded_attempts = 0; | ||
| let mut last_error = None; | ||
| let max_retries = self.retry_config.max_retries; | ||
| let mut retried_auth = false; | ||
|
|
||
| loop { | ||
| if rate_limit_attempts > max_retries && overloaded_attempts > max_retries { | ||
|
|
@@ -295,10 +296,21 @@ impl GcpVertexAIProvider { | |
| ); | ||
| } | ||
|
|
||
| let auth_header = self | ||
| .get_auth_header() | ||
| .await | ||
| .map_err(|e| ProviderError::Authentication(e.to_string()))?; | ||
| let auth_header = match self.get_auth_header().await { | ||
| Ok(header) => header, | ||
| Err(e) => { | ||
| if !retried_auth { | ||
| retried_auth = true; | ||
| if self.auth.refresh_credentials().await.is_ok() { | ||
| tracing::info!( | ||
| "gcloud token exchange failed ({e}); reloaded credentials and retrying" | ||
| ); | ||
| continue; | ||
| } | ||
| } | ||
| return Err(ProviderError::Authentication(e.to_string())); | ||
| } | ||
| }; | ||
|
|
||
| let mut request = self | ||
| .client | ||
|
|
@@ -355,6 +367,17 @@ impl GcpVertexAIProvider { | |
| } else if status == StatusCode::OK { | ||
| return Ok(response); | ||
| } else if status == StatusCode::UNAUTHORIZED || status == StatusCode::FORBIDDEN { | ||
| if !retried_auth { | ||
| retried_auth = true; | ||
| if let Err(e) = self.auth.refresh_credentials().await { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
This retry only runs after Vertex returns 401/403, so it misses the reauth path where the cached access token has already expired and the in-memory authorized-user refresh token is stale. In that case Useful? React with 👍 / 👎.
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
| tracing::warn!("Failed to reload gcloud credentials after {status}: {e}"); | ||
| } else { | ||
| tracing::info!( | ||
| "Vertex AI returned {status}; reloaded gcloud credentials and retrying" | ||
| ); | ||
| continue; | ||
| } | ||
| } | ||
| return Err(ProviderError::Authentication(format!( | ||
| "Authentication failed with status: {status}" | ||
| ))); | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the concurrent reauth path, this takes
credentials.write()and then waits forcached_token.write(), whileget_token()takescached_token.write()first and then awaitscredentials.read()for the token exchange. If a refresh is triggered while one request is refreshing a token and another is queued for the cache lock, the queuedget_token()can acquire the cache lock before this refresh does, then block on the credentials writer that is itself waiting for the cache lock, leaving both requests stuck. Avoid holding both locks in opposite orders, or clone the credentials under the read lock and drop it before the token-exchange await.Useful? React with 👍 / 👎.