Classify Claude CLI usage shell failures (#6065)

Co-authored-by: Orca <help@stably.ai>
This commit is contained in:
Jinwoo Hong
2026-06-22 00:58:32 -07:00
committed by GitHub
co-authored by Orca
parent b7a2488b8a
commit 24000561b4
3 changed files with 55 additions and 1 deletions
@@ -634,6 +634,34 @@ describe('fetchClaudeRateLimits', () => {
expect(fetchViaPty).toHaveBeenCalledWith({ authPreparation })
})
it('marks CLI plan usage shell results as usage unavailable', async () => {
const authPreparation: ClaudeRuntimeAuthPreparation = {
configDir: '/Users/test/.claude',
envPatch: {},
stripAuthEnv: false,
provenance: 'system'
}
vi.mocked(fetchViaPty).mockResolvedValueOnce({
provider: 'claude',
session: null,
weekly: null,
updatedAt: 1,
error: 'Claude plan usage is unavailable for this Claude CLI session.',
status: 'error'
})
await expect(fetchClaudeRateLimits({ authPreparation })).resolves.toMatchObject({
provider: 'claude',
status: 'error',
error: 'Claude plan usage is unavailable for this Claude CLI session.',
usageMetadata: {
source: 'cli',
attemptedSources: ['cli'],
failureKind: 'usage-unavailable'
}
})
})
it('surfaces Keychain read failures as structured usage metadata when CLI fallback is disabled', async () => {
vi.mocked(readActiveClaudeKeychainCredentials).mockRejectedValueOnce(
new Error('security timed out after 3000ms')
+17 -1
View File
@@ -443,6 +443,21 @@ function metadataForAttempt(input: {
}
}
function classifyClaudeCliUsageFailure(
limits: ProviderRateLimits
): UsageRateLimitFailureKind | undefined {
if (!limits.error) {
return undefined
}
if (/rate limited/i.test(limits.error)) {
return 'rate-limited'
}
if (/plan usage is unavailable|usage is unavailable/i.test(limits.error)) {
return 'usage-unavailable'
}
return 'cli-unavailable'
}
async function fetchClaudeUsageViaCli(input: {
authPreparation?: ClaudeRuntimeAuthPreparation
oauthCredentials: OAuthCredentialReadResult
@@ -456,7 +471,8 @@ async function fetchClaudeUsageViaCli(input: {
attemptedSources: input.attempts.attemptedSources,
oauthCredentials: input.oauthCredentials,
authPreparation: input.authPreparation,
source: 'cli'
source: 'cli',
failureKind: classifyClaudeCliUsageFailure(limits)
})
)
}
@@ -212,6 +212,16 @@ describe('provider usage error copy', () => {
expect(getProviderUsageStatusLabel(p)).toBe('Sign-in unavailable')
expect(getProviderUsageErrorMessage(p)).toBe('Claude sign-in credentials could not be read.')
})
it('uses structured unavailable copy for Claude CLI usage shell failures', () => {
const p = provider({
error: 'Claude plan usage is unavailable for this Claude CLI session.',
usageMetadata: { failureKind: 'usage-unavailable' }
})
expect(getProviderUsageStatusLabel(p)).toBe('Usage unavailable')
expect(getProviderUsageErrorMessage(p)).toBe('Claude usage is unavailable right now.')
})
})
describe('getWindowSections', () => {