Skip to content

fix(auth): clear recovered provider error state on real success paths - #372

Merged
diegosouzapw merged 5 commits into
cheaper-inference:mainfrom
kfiramar:fix/clear-provider-error-state
Mar 14, 2026
Merged

diegosouzapw merged 5 commits into
cheaper-inference:mainfrom
kfiramar:fix/clear-provider-error-state

Conversation

@kfiramar

Copy link
Copy Markdown
Contributor

Summary

  • return errorCode, lastErrorType, and lastErrorSource from getProviderCredentials() so recovery cleanup can act on the real stored state
  • clear stale recovered-state metadata when a provider request actually succeeds across the non-chat provider routes that previously never reset it
  • add focused regression coverage for both the account-level cleanup path and representative route-level success paths

Problem

Recovered provider accounts could remain marked with stale failure metadata even after a successful request.

In practice, that meant a connection could be back to testStatus=active while still retaining fields such as:

  • error_code=refresh_failed
  • last_error_type=token_refresh_failed
  • last_error_source=oauth

That left the stored provider state inconsistent with the provider's actual recovered condition.

Root Cause

There were two gaps.

First, clearAccountError() was being asked to decide whether cleanup was needed based on the current credentials object, but getProviderCredentials() was not returning all of the relevant persisted error metadata. In the real runtime path, the cleanup check therefore had incomplete information.

Second, chat-style routes already had success callbacks in the handler stack, but multiple other provider-backed success routes did not clear recovered error state at all. A successful embeddings, moderation, image, audio, rerank, music, or video request could therefore leave stale failure markers behind indefinitely.

Why This Fix

This PR keeps the fix scoped to actual recovery semantics:

  • it does not clear anything until a real provider request succeeds
  • it does not broaden behavior beyond routes that already resolve provider credentials and can therefore prove provider/account recovery
  • it reuses a single helper for post-success cleanup instead of duplicating per-route logic

Notably, cache hits are intentionally unchanged. A semantic-cache hit does not prove the provider recovered, so it should not clear provider failure state.

Proof

I verified the bug and the fix at three levels.

  1. Data-path proof
    On the pre-fix code path, getProviderCredentials() did not include errorCode, lastErrorType, or lastErrorSource, so the recovery cleanup path could not reliably clear the real stored metadata.

  2. Runtime proof against real stored state
    I verified a local provider row with:

  • test_status=active
  • stale refresh-failure metadata still present

Then I exercised a non-cached successful provider request and confirmed the patched branch clears the stale fields afterward.

  1. Route-surface proof
    I re-checked the src/app/api/v1 surface and confirmed there are no remaining provider-backed routes using getProviderCredentials() that still omit recovery clearing after a genuine success. The patched branch covers both success styles already present in the codebase:
  • result.success
  • response.ok

Verification

  • ran:
    node --import ./node_modules/tsx/dist/esm/index.mjs --test tests/unit/auth-clear-account-error.test.mjs tests/unit/auth-clear-provider-routes.test.mjs
  • built the branch successfully with PORT=20132 npm run build
  • verified the cleanup behavior against a real local provider row with stale refresh-failure metadata
  • re-audited the provider-backed src/app/api/v1 routes to confirm the success-path coverage is complete and limited to genuine provider success

Clear errorCode, lastErrorType, and lastErrorSource when an
account recovers so provider state returns to a fully clean
active status.

Add a focused regression test for recovered-account cleanup.
Pass errorCode, lastErrorType, and lastErrorSource through the
runtime credentials object so clearAccountError can clear stale
provider error metadata after a real successful request.

Also update the regression test to use getProviderCredentials,
matching the production call path.
Refine the recovered-account regression test to match the real
observed state: an account can remain active while still carrying
stale refresh-failure metadata.

This verifies that getProviderCredentials surfaces those fields
and that clearAccountError clears them through the real runtime
path.
Clear recovered provider error metadata after successful
credentialed requests in non-chat API routes as well.

Add route-level regression tests covering a Response-based
success path and a result-object success path.
@gemini-code-assist

Copy link
Copy Markdown
Contributor

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request resolves an issue where provider accounts could retain outdated error metadata even after successfully recovering from previous failures. By enriching the getProviderCredentials function with more detailed error information and integrating a new clearRecoveredProviderState helper into various API routes, the system now ensures that all relevant error flags are consistently cleared when a provider request succeeds. This change significantly improves the accuracy and consistency of the stored provider state, reflecting its true operational status.

Highlights

  • Enhanced Error Metadata Retrieval: The getProviderCredentials function now returns additional error metadata, including errorCode, lastErrorType, and lastErrorSource, enabling more comprehensive assessment of a provider's recovery state.
  • Automated Stale Error State Clearing: Implemented logic across various non-chat API routes (audio, embeddings, images, moderations, music, rerank, video) to automatically clear stale recovered-state metadata upon a genuinely successful provider request.
  • Improved Error Clearing Logic: The clearAccountError function has been updated to consider and clear the newly exposed error metadata, ensuring a more thorough cleanup of provider connection states.
  • Comprehensive Test Coverage: New unit tests have been added to specifically cover the account-level error clearing mechanism and to verify that error metadata is correctly cleared on successful calls to representative API routes.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Changelog
  • src/app/api/v1/audio/speech/route.ts
    • Imported clearRecoveredProviderState from the auth service.
    • Called clearRecoveredProviderState after a successful handleAudioSpeech operation.
  • src/app/api/v1/audio/transcriptions/route.ts
    • Imported clearRecoveredProviderState from the auth service.
    • Called clearRecoveredProviderState after a successful handleAudioTranscription operation.
  • src/app/api/v1/embeddings/route.ts
    • Imported clearRecoveredProviderState from the auth service.
    • Called clearRecoveredProviderState after a successful handleEmbedding operation.
  • src/app/api/v1/images/generations/route.ts
    • Imported clearRecoveredProviderState from the auth service.
    • Called clearRecoveredProviderState after a successful handleImageGeneration operation.
  • src/app/api/v1/moderations/route.ts
    • Imported clearRecoveredProviderState from the auth service.
    • Called clearRecoveredProviderState after a successful handleModeration operation.
  • src/app/api/v1/music/generations/route.ts
    • Imported clearRecoveredProviderState from the auth service.
    • Called clearRecoveredProviderState after a successful handleMusicGeneration operation.
  • src/app/api/v1/providers/[provider]/embeddings/route.ts
    • Imported clearRecoveredProviderState from the auth service.
    • Called clearRecoveredProviderState after a successful provider-specific handleEmbedding operation.
  • src/app/api/v1/providers/[provider]/images/generations/route.ts
    • Imported clearRecoveredProviderState from the auth service.
    • Called clearRecoveredProviderState after a successful provider-specific handleImageGeneration operation.
  • src/app/api/v1/rerank/route.ts
    • Imported clearRecoveredProviderState from the auth service.
    • Called clearRecoveredProviderState after a successful handleRerank operation.
  • src/app/api/v1/videos/generations/route.ts
    • Imported clearRecoveredProviderState from the auth service.
    • Called clearRecoveredProviderState after a successful handleVideoGeneration operation.
  • src/sse/services/auth.ts
    • Updated ProviderConnectionView interface to include lastErrorType and lastErrorSource.
    • Modified toProviderConnection to correctly map lastErrorType and lastErrorSource from database rows.
    • Updated getProviderCredentials to return errorCode, lastErrorType, and lastErrorSource.
    • Expanded clearAccountError to check for and clear errorCode, lastErrorType, and lastErrorSource.
    • Added a new clearRecoveredProviderState helper function to encapsulate error clearing logic.
  • tests/unit/auth-clear-account-error.test.mjs
    • Added a new unit test file to verify that clearAccountError correctly clears stale error metadata.
  • tests/unit/auth-clear-provider-routes.test.mjs
    • Added a new unit test file to verify that moderation and embeddings routes clear stale error metadata on successful calls.
Activity
  • The author identified a problem where recovered provider accounts retained stale failure metadata, leading to inconsistent stored states.
  • Two root causes were pinpointed: getProviderCredentials() not returning all relevant error metadata, and several non-chat routes lacking logic to clear recovered error states.
  • The proposed solution focuses on scoped recovery semantics, clearing errors only upon genuine provider request success and reusing a single helper for post-success cleanup.
  • Verification was conducted at three levels: data-path proof, runtime proof against real stored state, and route-surface proof.
  • Unit tests for auth-clear-account-error.test.mjs and auth-clear-provider-routes.test.mjs were executed.
  • The branch was successfully built, and the cleanup behavior was verified against a local provider row with stale refresh-failure metadata.
  • An audit of provider-backed src/app/api/v1 routes confirmed complete success-path coverage for error clearing.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution. ↩

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review

This pull request effectively addresses a bug where stale error metadata persisted for provider accounts even after successful requests. The fix is well-implemented by enhancing getProviderCredentials to return the full error state and then clearing this state in the relevant API routes upon success. The inclusion of comprehensive regression tests for both the account-level cleanup and route-level success paths is commendable and significantly increases confidence in the fix. My feedback focuses on two areas for potential improvement: enhancing type safety in the authentication service to move away from any types, and abstracting the duplicated success-handling logic from the API routes to improve maintainability.

Comment on lines +78 to +82
const response = await handleAudioSpeech({ body, credentials });
if (response?.ok) {
await clearRecoveredProviderState(credentials);
}
return response;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

This pattern of awaiting the handler, checking for success, and then clearing the provider state is repeated across multiple route files (e.g., audio/transcriptions, moderations, rerank). To reduce code duplication and improve maintainability, you could consider abstracting this logic into a higher-order function or a wrapper.

For example, you could create a helper in auth.ts:

// In sse/services/auth.ts
export async function handleRequestAndClearState(
  handlerPromise: Promise<Response>,
  credentials: any
): Promise<Response> {
  const response = await handlerPromise;
  if (response?.ok) {
    await clearRecoveredProviderState(credentials);
  }
  return response;
}

Then, the route handler could be simplified to:

// In route.ts
return handleRequestAndClearState(
  handleAudioSpeech({ body, credentials }),
  credentials
);

A similar helper could be created for the routes that check result.success.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Good maintainability point. I am intentionally keeping this PR scoped to the recovery bug fix itself rather than introducing a new wrapper abstraction across multiple route shapes. These handlers do not currently return a single common success shape (Response vs { success, data }), so abstracting this further would broaden the change beyond the targeted fix.

Comment thread src/sse/services/auth.ts Resolved
Comment thread src/sse/services/auth.ts Resolved
@@ -126,6 +131,7 @@ export async function POST(request) {
const result = await handleEmbedding({ body, credentials, log });

if (result.success) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

WARNING: Inconsistent response handling pattern. This route checks result.success while other routes in this PR (speech, transcriptions, moderations, rerank) check response?.ok. Consider standardizing the approach for maintainability.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I am leaving this as-is intentionally. The routes touched here already sit on top of different handler contracts: some return a structured { success, data } result, while others return a Response. This PR only adds recovered-state cleanup on the existing success signal for each route. Standardizing those handler return shapes would be a broader refactor than this bug-fix PR.


return handleModeration({ body: { ...body, model }, credentials });
const response = await handleModeration({ body: { ...body, model }, credentials });
if (response?.ok) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

WARNING: Inconsistent response handling pattern. This route checks response?.ok while other routes in this PR (embeddings, images, music, videos) check result.success. Consider standardizing the approach for maintainability.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Same reasoning here: this PR is using each route's existing success contract rather than trying to normalize handler return types across unrelated endpoints. I agree consistency would be nicer long-term, but I do not want to widen this fix into a cross-handler refactor.

return_documents: body.return_documents,
credentials,
});
if (response?.ok) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

WARNING: Inconsistent response handling pattern. This route checks response?.ok while other routes in this PR (embeddings, images, music, videos) check result.success. Consider standardizing the approach for maintainability.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Same reasoning here as on the other route comments: I am intentionally keying recovery cleanup off each route's current success contract (response.ok vs result.success) and not changing upstream handler return conventions inside this PR. That would be a broader maintainability refactor rather than part of the recovery-state bug fix itself.

@kilo-code-bot

kilo-code-bot Bot commented Mar 14, 2026 •

Copy link
Copy Markdown

Code Review Summary

Status: 4 Issues Found (Unchanged) | Recommendation: Address before merge

Overview

Severity Count
CRITICAL 0
WARNING 3
SUGGESTION 1
Issue Details (click to expand)

WARNING

File Line Issue
src/app/api/v1/embeddings/route.ts 133 Inconsistent response handling - uses result.success while other routes use response?.ok
src/app/api/v1/moderations/route.ts 73 Inconsistent response handling - uses response?.ok while other routes use result.success
src/app/api/v1/rerank/route.ts 82 Inconsistent response handling - uses response?.ok while other routes use result.success

SUGGESTION

File Line Issue
src/sse/services/auth.ts 604 Type safety - credentials: any should use a proper type definition
Other Observations (not in diff)

No new issues found in this PR review. The changes implement a new clearRecoveredProviderState function that clears stale provider error metadata after successful requests. The implementation is correct and follows the existing patterns in the codebase.

Files Reviewed (14 files)
  • src/app/api/v1/audio/speech/route.ts - Added clearRecoveredProviderState call
  • src/app/api/v1/audio/transcriptions/route.ts - Added clearRecoveredProviderState call
  • src/app/api/v1/embeddings/route.ts - Added clearRecoveredProviderState call
  • src/app/api/v1/images/generations/route.ts - Added clearRecoveredProviderState call
  • src/app/api/v1/moderations/route.ts - Added clearRecoveredProviderState call
  • src/app/api/v1/music/generations/route.ts - Added clearRecoveredProviderState call
  • src/app/api/v1/rerank/route.ts - Added clearRecoveredProviderState call
  • src/app/api/v1/videos/generations/route.ts - Added clearRecoveredProviderState call
  • src/app/api/v1/providers/[provider]/embeddings/route.ts - Added clearRecoveredProviderState call
  • src/app/api/v1/providers/[provider]/images/generations/route.ts - Added clearRecoveredProviderState call
  • src/sse/services/auth.ts - Added new RecoverableConnectionState interface and clearRecoveredProviderState function
  • tests/unit/auth-clear-account-error.test.mjs - New test file
  • tests/unit/auth-clear-provider-routes.test.mjs - New test file

Tighten the helper signatures added for recovered provider cleanup.

This removes the new any-typed recovery parameters called out in
review without broadening the PR into unrelated auth typing work.
@diegosouzapw
diegosouzapw merged commit 4552311 into cheaper-inference:main Mar 14, 2026
diegosouzapw added a commit that referenced this pull request Mar 14, 2026
bug fixes (PRs #373, #371, #372, #369 by @kfiramar):
- fix(db): provider_connections.group column migration for existing DBs
- fix(i18n): replace missing deleteConnection key with delete in tooltip
- fix(auth): clear stale error metadata on genuine provider recovery
- fix(startup): unify env loading across npm/electron startup paths

code quality improvements (per kilo-code-bot review):
- docs: document result.success vs response.ok patterns in auth.ts
- refactor: normalize overridePath?.trim() in electron/main.js
- docs: explain preferredEnv merge order intent
prakersh pushed a commit to prakersh/OmniRoute that referenced this pull request Mar 26, 2026
…ider-error-state

Thanks @kfiramar! 🎉 Critical fix — stale error metadata on recovered provider accounts was preventing valid accounts from being selected properly after recovery. 

Improvement added on top: documented the two valid success-check patterns (`result.success` for open-sse handlers vs `response?.ok` for fetch-based handlers) to address the kilo-code-bot review warning — both patterns are correct by design, now explicitly documented.

5 commits total, 2 test files (+168 lines of coverage). Merged!
prakersh pushed a commit to prakersh/OmniRoute that referenced this pull request Mar 26, 2026
bug fixes (PRs cheaper-inference#373, cheaper-inference#371, cheaper-inference#372, cheaper-inference#369 by @kfiramar):
- fix(db): provider_connections.group column migration for existing DBs
- fix(i18n): replace missing deleteConnection key with delete in tooltip
- fix(auth): clear stale error metadata on genuine provider recovery
- fix(startup): unify env loading across npm/electron startup paths

code quality improvements (per kilo-code-bot review):
- docs: document result.success vs response.ok patterns in auth.ts
- refactor: normalize overridePath?.trim() in electron/main.js
- docs: explain preferredEnv merge order intent
Poid-ZA pushed a commit to Poid-ZA/OmniRoute that referenced this pull request Aug 5, 2026
…ider-error-state

Thanks @kfiramar! 🎉 Critical fix — stale error metadata on recovered provider accounts was preventing valid accounts from being selected properly after recovery. 

Improvement added on top: documented the two valid success-check patterns (`result.success` for open-sse handlers vs `response?.ok` for fetch-based handlers) to address the kilo-code-bot review warning — both patterns are correct by design, now explicitly documented.

5 commits total, 2 test files (+168 lines of coverage). Merged!
Poid-ZA pushed a commit to Poid-ZA/OmniRoute that referenced this pull request Aug 5, 2026
bug fixes (PRs cheaper-inference#373, cheaper-inference#371, cheaper-inference#372, cheaper-inference#369 by @kfiramar):
- fix(db): provider_connections.group column migration for existing DBs
- fix(i18n): replace missing deleteConnection key with delete in tooltip
- fix(auth): clear stale error metadata on genuine provider recovery
- fix(startup): unify env loading across npm/electron startup paths

code quality improvements (per kilo-code-bot review):
- docs: document result.success vs response.ok patterns in auth.ts
- refactor: normalize overridePath?.trim() in electron/main.js
- docs: explain preferredEnv merge order intent
muhamadgalihsaputra pushed a commit to niyatna/NiyatnaRoute that referenced this pull request Sep 27, 2026
…ider-error-state

Thanks @kfiramar! 🎉 Critical fix — stale error metadata on recovered provider accounts was preventing valid accounts from being selected properly after recovery. 

Improvement added on top: documented the two valid success-check patterns (`result.success` for open-sse handlers vs `response?.ok` for fetch-based handlers) to address the kilo-code-bot review warning — both patterns are correct by design, now explicitly documented.

5 commits total, 2 test files (+168 lines of coverage). Merged!
muhamadgalihsaputra pushed a commit to niyatna/NiyatnaRoute that referenced this pull request Sep 27, 2026
bug fixes (PRs cheaper-inference#373, cheaper-inference#371, cheaper-inference#372, cheaper-inference#369 by @kfiramar):
- fix(db): provider_connections.group column migration for existing DBs
- fix(i18n): replace missing deleteConnection key with delete in tooltip
- fix(auth): clear stale error metadata on genuine provider recovery
- fix(startup): unify env loading across npm/electron startup paths

code quality improvements (per kilo-code-bot review):
- docs: document result.success vs response.ok patterns in auth.ts
- refactor: normalize overridePath?.trim() in electron/main.js
- docs: explain preferredEnv merge order intent
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants