Skip to content

fix(oauth): add google-antigravity to OAuth 401 replay and force-refresh providers #3575

Closed

Description

@agentHits

Client or integration

Direct HTTP/API client

Area

Authentication and account pool

Summary

When Google Antigravity OAuth session tokens expire, desynchronize, or are invalidated upstream by Google, incoming requests to OpenCodeX fail immediately with 401 instead of being refreshed and replayed transparently.

While other OAuth providers (xai, github-copilot, kiro) participate in automatic upstream 401 recovery (isOAuth401ReplayProvider and FORCE_REFRESH_PROVIDERS), google-antigravity is missing from both sets. Consequently, any 401 from upstream terminates the request immediately and leaves the stale token in ~/.opencodex/auth.json until a manual command or request triggers a refresh.

Reproduction

  1. Configure google-antigravity as an OAuth provider in OpenCodeX (ocx login google-antigravity).
  2. Allow the session token to expire, rotate in another environment, or become invalidated upstream.
  3. Send a request to /v1/chat/completions or /v1/responses for model google-antigravity/gemini-3.8-flash.
  4. Upstream Google API returns 401 UNAUTHENTICATED (Request had invalid authentication credentials).
  5. OpenCodeX aborts immediately and forwards the 401 error to the caller, without attempting to refresh the token or replay the request.

Expected behavior: OpenCodeX should catch the upstream 401, execute forceRefreshOAuthAccessSnapshot (refreshing credentials and re-discovering the Cloud Code Assist project), and replay the request to completion.

Version

dev / 2.36.1+

Operating system

macOS 15.5 / Linux / Windows

Provider and model

google-antigravity / gemini-3.8-flash

Logs or error output

[opencodex] HTTP 401 Unauthorized from daily-cloudcode-pa.googleapis.com
authentication_error: Request had invalid authentication credentials.

Redacted configuration

{
  "defaultProvider": "google-antigravity",
  "providers": {
    "google-antigravity": {
      "adapter": "google",
      "baseUrl": "https://daily-cloudcode-pa.googleapis.com",
      "authMode": "oauth",
      "googleMode": "cloud-code-assist",
      "models": ["gemini-3.8-flash"]
    }
  }
}

Checks

  • I searched existing issues and documentation.
  • I removed secrets, tokens, account details, request credentials, and personal data.

Activity

  1. added
    bugSomething isn't working
    account-poolOAuth, credentials, Codex pool, quota, failover, plans
    on Sep 5, 2026
  2. Ingwannu commented on Sep 5, 2026

    @Ingwannu
    Owner

    Confirmed against current dev at 24cc558d5: google-antigravity is absent from both the forced-refresh provider set and the Responses OAuth 401 replay predicate, even though its OAuth implementation already refreshes the token and re-discovers/preserves the Cloud Code Assist project. A locally valid-looking snapshot can therefore receive an upstream 401 and terminate without the existing one-refresh/one-replay recovery used by xAI, Copilot, and Kiro.

    The linked #3576 is aimed at the correct boundaries: add the exact built-in provider to both gates and replay with the refreshed token/project snapshot while preserving one-shot retry, singleflight, and sanitized errors. I am keeping the issue open until that security-sensitive PR is rebased onto the latest dev, reviewed at the new exact head, and merged.

  3. lidge-jun commented on Sep 5, 2026

    @lidge-jun
    Owner

    리뷰 · 우선순위 64 / 80

    이 이슈는 Google Antigravity OAuth 세션이 만료·무효화·다른 환경에서 회전됐을 때, 업스트림이 401을 주면 OpenCodex가 토큰을 강제 갱신하고 같은 요청을 한 번 더 보내지 않은 채 바로 401을 호출자에게 넘긴다는 버그다. 제보 재현은 ocx login google-antigravity 뒤 세션이 죽은 상태에서 /v1/chat/completions 또는 /v1/responses로 google-antigravity/gemini-3.8-flash를 치면 daily-cloudcode-pa.googleapis.com이 401 UNAUTHENTICATED를 내고, 프록시는 갱신·리플레이 없이 그 401을 그대로 올린다는 것이다. 기대한 동작은 forceRefreshOAuthAccessSnapshot으로 자격 증명을 돌리고 Cloud Code Assist projectId를 다시 맞춘 뒤 한 번 재시도하는 것이다.

    지금 dev(HEAD 24cc558d5, package 2.43.0)를 보면 제보가 가리킨 두 군데가 그대로다. src/server/responses/core.ts 3329줄 isOAuth401ReplayProvider는 xai / github-copilot / kiro만이고, route.provider.authMode === "oauth"일 때만 true다. 그 플래그가 true일 때만 sentOAuthSnapshot을 저장하고(3480줄), 네이티브 Responses 패스스루 401 분기(4357줄대)와 어댑터 recovery 루프 401 분기(6109줄대)가 forceRefreshOAuthAccessSnapshot을 호출한다. google-antigravity는 이름에 없어서 업스트림 401이 오면 리플레이 없이 끊긴다. 같은 allowlist가 src/oauth/index.ts 550줄 FORCE_REFRESH_PROVIDERS에도 있고, 555줄에서 집합에 없으면 UnsupportedOAuthProviderError를 던진다. 응답 경로에 이름만 넣고 force-refresh 집합을 같이 안 열면 갱신 호출 자체가 실패한다.

    Antigravity 쪽 OAuth 모듈은 이미 갱신 시 프로젝트를 다시 찾도록 되어 있다. src/oauth/google-antigravity.ts는 로그인·리프레시 모두 Cloud Code Assist projectId를 채우고, 스냅샷에도 실린다. 계정 페일오버 헬퍼 applyFailoverSnapshot(core.ts 3367–3381줄)은 route.provider.googleMode === "cloud-code-assist"에서 projectId 없으면 회전을 거부하고, 있으면 route.provider.project에 붙인다. 주석도 토큰만 바꾸고 실패한 계정의 project를 쓰면 더 나쁘다고 명시한다. 그런데 401 리플레이 경로(4376–4387, 6127–6138줄)는 지금 kiro의 parsed._kiroAuthContext만 특수 처리하고, provider 재조립은 { ...route.provider, apiKey: refreshed.accessToken }(+ Copilot baseUrl)만 한다. 여기에 google-antigravity만 allowlist에 추가하면 새 bearer와 옛 project가 짝이 남을 수 있다. 제보가 말한 refresh + re-discover project + replay를 끝내려면 allowlist 두 곳과 함께 리플레이 분기에서 refreshed.projectId를 route.provider.project에 넣는 선례(applyFailoverSnapshot과 동일)가 필요하다.

    최근 dev 방향과도 맞물린다. #3561/#3562가 #3502 캐리로 OAuth proactive-failover 경계와 Kiro rotated auth를 터미널 continuation에 실었고, Antigravity는 계정 풀·쿼타(#3213)와 generic pool-settings(#3214)는 있으나 업스트림 401 자동 리플레이 명단에는 아직 없다. #3375 같은 풀 라이프사이클 이슈와 겹치는 면이 있지만, 이 이슈는 범위가 allowlist + Antigravity 스냅샷 재적용으로 훨씬 작아서 그 대형 스펙을 기다릴 필요는 없다. 중복 이슈 검색에서도 같은 제목의 열린 항목은 #3575뿐이다.

    src/server/responses/core.ts 3329 - isOAuth401ReplayProvider에 google-antigravity가 없어 업스트림 401이 리플레이 없이 종료된다.
    src/oauth/index.ts 550–555 - FORCE_REFRESH_PROVIDERS에도 없어서 forceRefreshOAuthAccessSnapshot가 UnsupportedOAuthProviderError로 막힌다.
    src/server/responses/core.ts 4376–4387 / 6127–6138 - 401 리플레이가 apiKey(와 kiro/copilot 특수 필드)만 갱신하고 projectId→provider.project 재바인딩이 없다.
    src/server/responses/core.ts applyFailoverSnapshot 3371–3381 - cloud-code-assist용 project 가드·주입 선례. 401 리플레이도 재사용하거나 같은 규칙을 복사해야 한다.
    src/oauth/google-antigravity.ts refresh/discover - 프로젝트 재발견 로직은 이미 있음. 문제는 호출 경로가 allowlist 밖으로 막힌 것이다.

    메인테이너의 판단이 필요한 지점

    너의 추천
    라벨 bug/account-pool 맞다. help wanted로 두되 구현 스펙을 고정하자: (1) FORCE_REFRESH_PROVIDERS와 isOAuth401ReplayProvider에 google-antigravity 추가, (2) 두 401 리플레이 분기에서 refreshed.projectId를 route.provider.project에 반영(가능하면 applyFailoverSnapshot 재사용), (3) 만료 토큰 + 유효 refresh로 401→갱신→200 회귀 테스트. #3561/#3562 직후 OAuth 리플레이 맥락이 살아 있으니 작은 PR로 빨리 받는 걸 추천한다. 우선순위 64.

    이 댓글은 grok-bot이 작성했습니다

  4. agentHits commented on Sep 5, 2026

    @agentHits
    ContributorAuthor

    Thanks @Ingwannu. Pull request #3576 has been rebased directly onto dev (24cc558d5) and verified.

    The diff is strictly scoped to:

    1. Adding "google-antigravity" to FORCE_REFRESH_PROVIDERS in src/oauth/index.ts.
    2. Adding google-antigravity to isOAuth401ReplayProvider and synchronizing the refreshed projectId into refreshedProvider in both non-streaming and streaming 401 replay loops in src/server/responses/core.ts.
    3. Adding the focused regression test suite in tests/server/server-google-antigravity-oauth-401-replay.test.ts (all 7 tests passing).

    The review readiness checklist in the PR description is 4/4 complete, and CodeRabbit analysis is clean. Ready for trust-boundary review and maintainer-sponsored sponsorship.

  5. lidge-jun commented on Sep 5, 2026

    @lidge-jun
    Owner

    Implemented by #3691 and merged into dev as 7e7ab281cca35600b41f1f80222f3462a87dd4e1. Original author attribution is preserved. Current-head hosted CI and independent security review passed; remote focused verification: 208pass/0fail. Dev ancestry confirmed before closing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    account-poolOAuth, credentials, Codex pool, quota, failover, plansbugSomething isn't working

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions