Skip to content

fix(subscription): 避免临时刷新失败锁死凭据 - #499

Merged
tbphp merged 8 commits into
v2from
tbphp/subscription-refresh-recovery
Aug 30, 2026
Merged

fix(subscription): 避免临时刷新失败锁死凭据#499
tbphp merged 8 commits into
v2from
tbphp/subscription-refresh-recovery

Conversation

@tbphp

@tbphp tbphp commented Aug 30, 2026

Copy link
Copy Markdown
Owner

关联 Issue / Related Issue

变更内容 / Change Content

  • Bug 修复 / Bug fix

  • 新功能 / New feature

  • 其他改动 / Other changes

  • 区分 OAuth Token 端点的临时失败、确定失效与结果未知,避免明确的 429/5xx 等临时响应将订阅凭据永久锁定。

  • 临时失败时恢复凭据为 ready,数据面冷却当前凭据并切换下一候选,同时保留手动重试能力。

  • 网络中断、成功响应解析失败以及持久化或运行时同步失败继续进入 outcome_unknown,保持 fail closed。

  • 非 ready 凭据不再允许单个或批量刷新,并为临时失败、重新授权和结果未知提供明确提示。

  • 增加不包含 Token、响应正文或原始错误的结构化刷新失败日志,并修复 Grok 非 JSON Token 端点错误的状态识别。

验证:

  • make check
  • cd third_party/cpaembedded && go mod tidy -diff && go vet ./... && go test -count=1 ./...

兼容性:

  • 不涉及数据库 Schema 或数据迁移。
  • 管理 API 新增临时刷新失败响应:HTTP 503 / CREDENTIAL_REFRESH_TEMPORARILY_UNAVAILABLE
  • 本次行为修复无需更新公开文档或发布说明。

自查清单 / Checklist

  • 我已运行 make check,或在说明中写明无法运行的原因和未验证范围。 / I ran make check, or documented why it could not run and what remains unverified.
  • 本 PR 范围聚焦,未包含无关改动。 / This PR is focused and contains no unrelated changes.
  • 我已更新必要的公开文档或发布说明。 / I updated any required public documentation or release notes.
  • 我已确认提交、日志和测试数据不包含敏感信息。 / I confirmed that commits, logs, and fixtures contain no sensitive data.
  • 如适用,我已说明兼容性或数据迁移影响。 / Where applicable, I documented compatibility or data-migration impact.

Summary by CodeRabbit

  • 新功能

    • 凭据刷新遇到临时限流或服务不可用时,会提示“暂时不可用”,并支持稍后重试。
    • 可重试失败会保留凭据的就绪状态,自动冷却失败凭据并尝试其他可用凭据。
    • 新增“结果未知”状态,区分需要重新连接或重新导入的凭据。
  • 界面改进

    • 非就绪凭据将禁用刷新和批量同步操作。
    • 凭据刷新失败后会同步更新凭据列表及分组汇总。
    • 更新多语言提示文案。

Copilot AI lite review requested due to automatic review settings August 30, 2026 02:42

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@coderabbitai

coderabbitai Bot commented Aug 30, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

本次变更为凭据刷新增加可重试失败分类,并保留 HTTP 状态码、OAuth 错误码和重试等待时间。临时失败时,凭据阶段恢复为 Ready,系统返回临时不可用错误并记录结构化日志。执行判定会设置凭据冷却并重试其他凭据。订阅发现、执行适配器、网关测试和多项重试测试覆盖该流程。界面新增 outcome_unknown 状态,禁止非就绪凭据刷新或同步,并更新多语言错误文案。

Merge Risk: 🟡 Moderate · up to 87bbe

Successful manual credential refresh can leave the credential unavailable to normal traffic until an earlier cooldown expires, delaying service recovery. Refresh failure state may also be slow to appear in the management UI, and some provider failures may lose retry guidance; these bounded issues should be fixed or explicitly accepted before merging.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 5.32% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 94 functions across 54 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed 标题准确概括了本次修复的核心目标:避免订阅凭据因临时刷新失败而被永久锁定。标题简洁且与变更内容相关。
Description check ✅ Passed 描述包含变更内容、验证命令、兼容性说明和自查清单。关联 Issue 标记为“无”,未提供 issue 编号,但已明确说明无关联 Issue;其余模板要求基本完整。
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
✨ Simplify code
  • Create PR with simplified code
  • Commit simplified code in branch tbphp/subscription-refresh-recovery

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Aug 30, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-08-30T10:03:29.826288Z 8f0ea52 New commits
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note

Quiet mode is enabled, so only the most important comments were posted inline. Other review comments are grouped below.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
web/src/features/groups/credentials/GroupCredentialsTab.vue (1)

714-717: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

刷新失败后重新同步凭据状态。

refreshCredentialRequest 失败时,服务端仍可能已将 auth_state 更新为 readyreauthorization_requiredoutcome_unknown,但 catch 只更新 feedback,因此凭据列表和分组摘要会继续显示旧缓存。useApiClient 不会自动失效查询。请在失败分支使用 Promise.allSettled 重新获取当前凭据页和分组摘要。

🟡 Other comments (1)
web/src/i18n/locales/ja-JP/group.ts-550-550 (1)

550-550: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

修正日文 outcome_unknown 文案。

認証情報の更新結果不明です 缺少 ,日语表达不自然。認証情報の結果不明 又省略了“更新”,没有明确说明未知的是凭据刷新结果。请统一语义。

建议修改
-          outcome_unknown: '認証情報の更新結果不明です。再接続または再インポートしてください',
+          outcome_unknown: '認証情報の更新結果が不明です。再接続または再インポートしてください',
...
-          outcome_unknown: '認証情報の結果不明',
+          outcome_unknown: '認証情報の更新結果が不明',

Also applies to: 582-582


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: QUIET

Plan: Pro Plus

Run ID: 692abe5a-5b72-44a9-9d41-08682224f570

📥 Commits

Reviewing files that changed from the base of the PR and between aee2111 and 431fd1c.

📒 Files selected for processing (34)
  • internal/control/credential_actions_test.go
  • internal/control/credential_stages.go
  • internal/control/credential_stages_test.go
  • internal/control/discover_group_test.go
  • internal/control/discover_subscription.go
  • internal/control/discover_test.go
  • internal/execution/contracts.go
  • internal/gateway/handler_test.go
  • internal/health/execution_judge.go
  • internal/health/execution_judge_test.go
  • internal/platform/errors/errors.go
  • internal/subscription/credential_manager.go
  • internal/subscription/credential_manager_test.go
  • internal/subscription/providers/antigravity/driver.go
  • internal/subscription/providers/antigravity/driver_test.go
  • internal/subscription/providers/claude/driver.go
  • internal/subscription/providers/claude/driver_test.go
  • internal/subscription/providers/codex/driver.go
  • internal/subscription/providers/codex/driver_test.go
  • internal/subscription/providers/grok/driver.go
  • internal/subscription/providers/grok/driver_test.go
  • internal/subscription/runtime/runtime.go
  • internal/subscription/runtime/runtime_test.go
  • third_party/cpaembedded/embedded/grok_oauth.go
  • third_party/cpaembedded/embedded/grok_test.go
  • web/src/features/groups/credentials/GroupCredentialsTab.vue
  • web/src/features/groups/credentials/SubscriptionAccountCard.vue
  • web/src/features/subscription-error-presenter.ts
  • web/src/i18n/locales/en-US/core.ts
  • web/src/i18n/locales/en-US/group.ts
  • web/src/i18n/locales/ja-JP/core.ts
  • web/src/i18n/locales/ja-JP/group.ts
  • web/src/i18n/locales/zh-CN/core.ts
  • web/src/i18n/locales/zh-CN/group.ts

Included review availability: Your plan provides up to 8 included reviews per hour; 6 remain after this review.

@tbphp tbphp self-assigned this Aug 30, 2026
@tbphp tbphp added the bug Something isn't working label Aug 30, 2026
@tbphp tbphp added this to the v2.0.0 milestone Aug 30, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 6551cdf540

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread internal/subscription/credential_manager.go

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 75c2195eab

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread internal/subscription/credential_manager.go

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Note

Quiet mode is enabled, so only the most important comments were posted inline. Other review comments are grouped below.

🟡 Other comments (1)
third_party/cpaembedded/embedded/grok_oauth.go-127-127 (1)

127-127: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

先处理失败状态,再解析设备令牌响应。

当设备令牌端点返回 HTTP 503 和非 JSON 响应体时,Line 127 获得的 statusretryAfter 会在 Line 133 的 json.Unmarshal 失败后丢失。调用方只会收到通用解码错误,不能识别临时端点失败或使用冷却时间。将 status != http.StatusOK 分支移到 JSON 解析之前,并在该分支中尽力提取 OAuth 错误码。

建议修改
 	defer clear(body)
 	var token grokTokenResponse
+	if status != http.StatusOK {
+		_ = json.Unmarshal(body, &token)
+		return GrokDevicePoll{}, &GrokTokenEndpointError{
+			StatusCode: status, Code: boundedGrokOAuthCode(token.Error), RetryAfter: retryAfter,
+		}
+	}
 	if err := json.Unmarshal(body, &token); err != nil {
 		return GrokDevicePoll{}, fmt.Errorf("decode Grok device token response: %w", err)
 	}

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: QUIET

Plan: Pro Plus

Run ID: 6a1911d0-7fcc-4fea-83e4-b6c0451c1fd8

📥 Commits

Reviewing files that changed from the base of the PR and between 6551cdf and 75c2195.

📒 Files selected for processing (30)
  • internal/health/execution_judge_test.go
  • internal/subscription/credential_manager.go
  • internal/subscription/credential_manager_test.go
  • internal/subscription/providers/antigravity/antigravity.go
  • internal/subscription/providers/antigravity/antigravity_test.go
  • internal/subscription/providers/antigravity/driver.go
  • internal/subscription/providers/antigravity/driver_test.go
  • internal/subscription/providers/claude/claude.go
  • internal/subscription/providers/claude/claude_test.go
  • internal/subscription/providers/claude/driver.go
  • internal/subscription/providers/claude/driver_test.go
  • internal/subscription/providers/codex/codex.go
  • internal/subscription/providers/codex/codex_test.go
  • internal/subscription/providers/codex/driver.go
  • internal/subscription/providers/codex/driver_test.go
  • internal/subscription/providers/grok/driver.go
  • internal/subscription/providers/grok/driver_test.go
  • internal/subscription/providers/grok/grok.go
  • internal/subscription/runtime/runtime.go
  • third_party/cpaembedded/embedded/antigravity_oauth.go
  • third_party/cpaembedded/embedded/antigravity_test.go
  • third_party/cpaembedded/embedded/claude_oauth.go
  • third_party/cpaembedded/embedded/claude_test.go
  • third_party/cpaembedded/embedded/embedded.go
  • third_party/cpaembedded/embedded/embedded_test.go
  • third_party/cpaembedded/embedded/grok.go
  • third_party/cpaembedded/embedded/grok_oauth.go
  • third_party/cpaembedded/embedded/grok_test.go
  • third_party/cpaembedded/embedded/oauth_retry_after.go
  • third_party/cpaembedded/embedded/oauth_retry_after_test.go

Included review availability: Your plan provides up to 8 included reviews per hour; 6 remain after this review.

Comment thread internal/subscription/providers/antigravity/driver.go

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 8d00cc1031

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread internal/subscription/credential_manager.go

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 87bbeabbb9

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread internal/subscription/credential_manager.go

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: QUIET

Plan: Pro Plus

Run ID: e343e649-dceb-4182-bbad-1e06a2d94c43

📥 Commits

Reviewing files that changed from the base of the PR and between 8d00cc1 and 87bbeab.

📒 Files selected for processing (6)
  • internal/control/service.go
  • internal/gateway/handler.go
  • internal/state/registry.go
  • internal/subscription/credential_manager.go
  • internal/subscription/credential_manager_test.go
  • internal/subscription/runtime/runtime.go

Included review availability: Your plan provides up to 8 included reviews per hour; 7 remain after this review.

Comment thread internal/subscription/credential_manager.go

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 8a4f48d6c7

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread internal/subscription/credential_manager.go

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c2b210cbe9

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread internal/gateway/handler.go Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 8f0ea52ddf

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +323 to +324
if !bypassedCooldown.IsZero() {
manager.registry.ClearCooldownIfMatch(row.ID, bypassedCooldown)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve unrelated cooldowns after manual refresh

When a credential is cooling down because the model endpoint returned 429 or another health decision set EffectCooldownCredential, a successful control-plane token refresh reaches this code with that same shared deadline in bypassedCooldown. ClearCooldownIfMatch cannot distinguish model-request cooldowns from token-refresh cooldowns, so clicking “refresh credential” clears the upstream model cooldown and makes the credential immediately selectable again, potentially violating its Retry-After. Track cooldown provenance or clear only deadlines created by token-refresh failures.

Useful? React with 👍 / 👎.

@tbphp
tbphp merged commit 922c6e0 into v2 Aug 30, 2026
7 checks passed
@tbphp
tbphp deleted the tbphp/subscription-refresh-recovery branch August 30, 2026 10:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants