kimi-code/packages/oauth/src/errors.ts
7Sageer 843a731097
fix: classify OAuth token refresh errors by cause (#838)
Map OAuth token-fetch failures to distinct public error codes instead of collapsing them all to auth.login_required:

- missing/revoked tokens or 401/403 from the refresh endpoint -> auth.login_required
- transport failures and 429/5xx after internal retries -> provider.connection_error
- anything else is rethrown as-is (surfaces as internal) rather than guessed

The refresh helper already retries internally, so the agent loop does not
re-retry these. Both the managed provider and the standalone SDK provider
share the same mapping.
2026-06-17 18:34:42 +08:00

58 lines
1.7 KiB
TypeScript

/**
* OAuth error classes.
*
* All errors derive from {@link OAuthError}. Distinguishing subclasses let
* callers react appropriately:
* - `OAuthUnauthorizedError`: 401/403 from token endpoint → refresh_token
* or credentials are bad; drive user through `/login` again.
* - `OAuthConnectionError`: transport-level OAuth request failure; callers
* may retry the operation.
* - `DeviceCodeExpiredError`: device_code TTL ran out before user approved;
* restart the device flow.
* - `DeviceCodeTimeoutError`: local 15 min wall-clock budget exhausted
* before the user completed approval.
* - `RetryableRefreshError`: 429 / 5xx from token endpoint; the refresh
* helper retries with exponential backoff before surfacing this.
*/
export class OAuthError extends Error {
constructor(message: string) {
super(message);
this.name = 'OAuthError';
}
}
export class OAuthUnauthorizedError extends OAuthError {
constructor(message: string) {
super(message);
this.name = 'OAuthUnauthorizedError';
}
}
export class OAuthConnectionError extends OAuthError {
constructor(message: string) {
super(message);
this.name = 'OAuthConnectionError';
}
}
export class DeviceCodeExpiredError extends OAuthError {
constructor(message = 'Device code expired.') {
super(message);
this.name = 'DeviceCodeExpiredError';
}
}
export class DeviceCodeTimeoutError extends OAuthError {
constructor(message = 'Device authorization timed out locally.') {
super(message);
this.name = 'DeviceCodeTimeoutError';
}
}
export class RetryableRefreshError extends OAuthError {
constructor(message: string) {
super(message);
this.name = 'RetryableRefreshError';
}
}