Google Vertex authentication flow fails upon finding GOOGLE_API_KEY env var
bug
Noticed this today when testing things on another PR.
``` r
library(ellmer)
chat <- chat_google_vertex(
location = Sys.getenv("GOOGLE_CLOUD_LOCATION"),
project_id = Sys.getenv("GOOGLE_CLOUD_PROJECT"),
model = "gemini-3.1-flash-lite-preview", params = params(reasoning_tokens = 1024)
)
chat$chat("What's 2+2")
#> Error in `req_perform_connection()`:
#> ! HTTP 401 Unauthorized.
#> • OAuth error
#> • realm: https://accounts.google.com/
#> ℹ API keys are not supported by this API. Expected OAuth2 access token or other
#> authentication credentials that assert a principal. See
#> https://cloud.google.com/docs/authentication
Sys.unsetenv("GOOGLE_API_KEY")
chat <- chat_google_vertex(
location = Sys.getenv("GOOGLE_CLOUD_LOCATION"),
project_id = Sys.getenv("GOOGLE_CLOUD_PROJECT"),
model = "gemini-3.1-flash-lite-preview", params = params(reasoning_tokens = 1024)
)
#> Warning: Unable to refresh token: invalid_grant
#> reauth related error (invalid_rapt)
#> https://support.google.com/a/answer/9368756
chat$chat("What's 2+2")
#> Error in `if (expiry < Sys.time()) ...`:
#> ! argument is of length zero
```
<sup>Created on 2026-05-22 with [reprex v2.1.1](https://reprex.tidyverse.org)</sup>
*Issue 1 - checking for API key existence*
Firstly, `default_google_credentials()` checks `GOOGLE_API_KEY` regardless of variant, and so when I have it set and call `chat_google_vertex()` it tries to send the API key even though I want 0Auth2.
As `chat_google_vertex()` doesn't expose `credentials` or `api_key`, we can just skip this block in `default_google_credentials()`.
(N.B. Google Vertex, which appears to be rebranded to Gemini Enterprise Agent Platform, [doesn't allow for using API keys except for in express mode](https://docs.cloud.google.com/gemini-enterprise-agent-platform/reference/express-mode/api-reference), which we don't implement here - there may be a case for doing so, but beyond the scope of this ticket)
*Issue 2 - checking for API key existence*
The second example actually was a workaround which did work, but failed when I switched IDEs and may have surfaced another bug.
Claude description:
> "After unsetting `GOOGLE_API_KEY`, `gargle::token_fetch()` tries to use cached credentials that need refreshing. The refresh fails with "invalid_grant", but gargle still returns the token (not NULL) in a bad state. Since token$can_refresh() is TRUE, we hit the final branch where expiry is computed from token$credentials$expires_in — but that's NULL after the failed refresh, so expiry becomes numeric(0). The subsequent `if (expiry < Sys.time())` then errors with "argument is of length zero".
> The root cause is a stale gargle token cache, but ellmer should handle this more gracefully (e.g. defaulting expires_in to 0 when NULL, so it forces an immediate refresh attempt)"
I'm thinking we want something like:
```r
if (!is.null(token) && is.null(token$credentials$access_token)) {
cli::cli_abort(c(
"Google credentials were found but are not valid.",
"i" = "Run {.code gcloud auth application-default login} to set up
application default credentials."
))
}
```
but without assuming the user is going to be using `gcloud` command line, so perhaps more generic.
We might also want to check for the existence of `GOOGLE_CLOUD_LOCATION` and `GOOGLE_CLOUD_PROJECT` for `chat_google_vertex()`, or just set the default location/project param values to these env vars? In #434, it looks like the reason we don't have them as default is because there was nothing in the docs indicating this, but now there's https://docs.cloud.google.com/workflows/docs/reference/environment-variables
1 条评论