diff --git a/.changeset/fix-model-auth-error-message.md b/.changeset/fix-model-auth-error-message.md new file mode 100644 index 000000000..944dbdd6d --- /dev/null +++ b/.changeset/fix-model-auth-error-message.md @@ -0,0 +1,5 @@ +--- +"@moonshot-ai/kimi-code": patch +--- + +Fix a misleading "OAuth login expired" message shown when a model is not available for the current account; the CLI now shows the provider's actual error. diff --git a/packages/agent-core/src/agent/compaction/full.ts b/packages/agent-core/src/agent/compaction/full.ts index ca1f19c8b..005da411f 100644 --- a/packages/agent-core/src/agent/compaction/full.ts +++ b/packages/agent-core/src/agent/compaction/full.ts @@ -595,7 +595,12 @@ export class FullCompaction { thinking_effort: this.agent.config.thinkingEffort, error_type: error instanceof Error ? error.name : 'Unknown', }); - if (isKimiError(error) && error.code === ErrorCodes.AUTH_LOGIN_REQUIRED) throw error; + if ( + isKimiError(error) && + (error.code === ErrorCodes.AUTH_LOGIN_REQUIRED || + error.code === ErrorCodes.PROVIDER_AUTH_ERROR) + ) + throw error; throw new KimiError(ErrorCodes.COMPACTION_FAILED, String(error), { cause: error }); } } diff --git a/packages/agent-core/src/session/provider-manager.ts b/packages/agent-core/src/session/provider-manager.ts index b67aef318..14a6fb5c9 100644 --- a/packages/agent-core/src/session/provider-manager.ts +++ b/packages/agent-core/src/session/provider-manager.ts @@ -203,9 +203,10 @@ export class ProviderManager implements ModelProvider { } catch (error) { if (!(error instanceof APIStatusError) || error.statusCode !== 401) throw error; if (refreshed) { + const reason = error.message.replaceAll('\r', ''); throw new KimiError( - ErrorCodes.AUTH_LOGIN_REQUIRED, - 'OAuth provider credentials were rejected. Send /login to login.', + ErrorCodes.PROVIDER_AUTH_ERROR, + reason.length > 0 ? reason : 'OAuth provider credentials were rejected.', { cause: error, details: { statusCode: error.statusCode, requestId: error.requestId }, diff --git a/packages/agent-core/test/agent/compaction/full.test.ts b/packages/agent-core/test/agent/compaction/full.test.ts index ff9f2ce76..61ab5dd13 100644 --- a/packages/agent-core/test/agent/compaction/full.test.ts +++ b/packages/agent-core/test/agent/compaction/full.test.ts @@ -362,7 +362,7 @@ describe('FullCompaction', () => { expect(messageText(compactionCall?.history[5])).toBe('lookup result'); }); - it('force-refreshes OAuth credentials on compaction 401 and falls back to login_required when replay 401', async () => { + it('force-refreshes OAuth credentials on compaction 401 and treats replay 401 as provider auth error', async () => { const tokenCalls: Array = []; const authKeys: string[] = []; const oauthOptions = oauthTestAgentOptions(async (options) => { @@ -398,7 +398,7 @@ describe('FullCompaction', () => { expect.objectContaining({ event: 'error', args: expect.objectContaining({ - code: 'auth.login_required', + code: 'provider.auth_error', details: expect.objectContaining({ statusCode: 401, requestId: 'req-compact-401', diff --git a/packages/agent-core/test/agent/turn.test.ts b/packages/agent-core/test/agent/turn.test.ts index 6463bf9e5..d4da3a957 100644 --- a/packages/agent-core/test/agent/turn.test.ts +++ b/packages/agent-core/test/agent/turn.test.ts @@ -1422,7 +1422,7 @@ describe('Agent turn flow', () => { ); }); - it('falls back to login_required when force-refresh and replay both 401', async () => { + it('treats 401 after force-refresh as provider auth error', async () => { const tokenCalls: Array = []; const authKeys: string[] = []; const oauthOptions = oauthAgentOptions( @@ -1460,7 +1460,7 @@ describe('Agent turn flow', () => { args: expect.objectContaining({ reason: 'failed', error: expect.objectContaining({ - code: 'auth.login_required', + code: 'provider.auth_error', details: expect.objectContaining({ statusCode: 401, requestId: 'req-401', @@ -1657,7 +1657,7 @@ describe('Agent turn flow', () => { expect(payloads[1]).toMatchObject({ turnStep: '0.1', attempt: '2/3' }); }); - it('force-refreshes OAuth credentials on video upload 401 and falls back to login_required when replay 401', async () => { + it('force-refreshes OAuth credentials on video upload 401 and surfaces the provider auth error when replay 401', async () => { const tokenCalls: Array = []; const authKeys: string[] = []; const oauthOptions = oauthAgentOptions( @@ -1702,8 +1702,9 @@ describe('Agent turn flow', () => { expect(result.isError).toBe(true); expect(authKeys).toEqual(['fresh-token', 'forced-refresh-token']); expect(tokenCalls).toEqual([undefined, true]); - expect(result.output).toContain('OAuth provider credentials were rejected'); - expect(result.output).toContain('Send /login to login'); + expect(result.output).toContain('Unauthorized'); + expect(result.output).not.toContain('OAuth provider credentials were rejected'); + expect(result.output).not.toContain('Send /login to login'); }); it('cancels an active turn', async () => {