Skip to content

Commit b63ed44

Browse files
committed
fix(sdk): handle AI SDK APICallError status property for Claude OAuth errors
The AI SDK uses `status` instead of `statusCode` for HTTP status codes. Updated getErrorStatusCode() to check both properties, ensuring proper error handling and fallback behavior for Claude OAuth rate limits and auth errors.
1 parent 9231f9b commit b63ed44

File tree

2 files changed

+27
-17
lines changed

2 files changed

+27
-17
lines changed

sdk/src/error-utils.ts

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -73,13 +73,24 @@ export function isRetryableStatusCode(statusCode: number | undefined): boolean {
7373
}
7474

7575
/**
76-
* Extracts the statusCode from an error if available
76+
* Extracts the statusCode from an error if available.
77+
* Checks both 'statusCode' (our convention) and 'status' (AI SDK's APICallError convention).
7778
*/
7879
export function getErrorStatusCode(error: unknown): number | undefined {
79-
if (error && typeof error === 'object' && 'statusCode' in error) {
80-
const statusCode = (error as { statusCode: unknown }).statusCode
81-
if (typeof statusCode === 'number') {
82-
return statusCode
80+
if (error && typeof error === 'object') {
81+
// Check 'statusCode' first (our convention)
82+
if ('statusCode' in error) {
83+
const statusCode = (error as { statusCode: unknown }).statusCode
84+
if (typeof statusCode === 'number') {
85+
return statusCode
86+
}
87+
}
88+
// Check 'status' (AI SDK's APICallError uses this)
89+
if ('status' in error) {
90+
const status = (error as { status: unknown }).status
91+
if (typeof status === 'number') {
92+
return status
93+
}
8394
}
8495
}
8596
return undefined

sdk/src/impl/llm.ts

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import {
1818
import { AnalyticsEvent } from '@codebuff/common/constants/analytics-events'
1919
import { getModelForRequest, markClaudeOAuthRateLimited, fetchClaudeOAuthResetTime } from './model-provider'
2020
import { getValidClaudeOAuthCredentials } from '../credentials'
21+
import { getErrorStatusCode } from '../error-utils'
2122

2223
import type { ModelRequestParams } from './model-provider'
2324
import type { OpenRouterProviderRoutingOptions } from '@codebuff/common/types/agent-template'
@@ -116,17 +117,15 @@ type OpenRouterUsageAccounting = {
116117
function isClaudeOAuthRateLimitError(error: unknown): boolean {
117118
if (!error || typeof error !== 'object') return false
118119

119-
// Check for APICallError from AI SDK
120+
// Check status code (handles both 'status' from AI SDK and 'statusCode' from our errors)
121+
const statusCode = getErrorStatusCode(error)
122+
if (statusCode === 429) return true
123+
124+
// Check error message for rate limit indicators
120125
const err = error as {
121-
statusCode?: number
122126
message?: string
123127
responseBody?: string
124128
}
125-
126-
// Check status code
127-
if (err.statusCode === 429) return true
128-
129-
// Check error message for rate limit indicators
130129
const message = (err.message || '').toLowerCase()
131130
const responseBody = (err.responseBody || '').toLowerCase()
132131

@@ -149,15 +148,15 @@ function isClaudeOAuthRateLimitError(error: unknown): boolean {
149148
function isClaudeOAuthAuthError(error: unknown): boolean {
150149
if (!error || typeof error !== 'object') return false
151150

151+
// Check status code (handles both 'status' from AI SDK and 'statusCode' from our errors)
152+
const statusCode = getErrorStatusCode(error)
153+
if (statusCode === 401 || statusCode === 403) return true
154+
155+
// Check error message for auth indicators
152156
const err = error as {
153-
statusCode?: number
154157
message?: string
155158
responseBody?: string
156159
}
157-
158-
// 401 Unauthorized or 403 Forbidden typically indicate auth issues
159-
if (err.statusCode === 401 || err.statusCode === 403) return true
160-
161160
const message = (err.message || '').toLowerCase()
162161
const responseBody = (err.responseBody || '').toLowerCase()
163162

0 commit comments

Comments
 (0)