Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion src/vs/workbench/contrib/chat/common/chatErrorMessages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -354,7 +354,19 @@ function isForwardedChatError(value: unknown): value is IForwardedChatError {
*/
export function getChatErrorDetailsFromMeta(error: ErrorInfo | undefined, context?: IChatErrorContext): IChatResponseErrorDetails | undefined {
const meta = error?._meta;
const chatError = meta?.chatError;
let chatError = meta?.chatError;
// In some hosts the forwarded chat error may be JSON-stringified. Accept
// either the raw object or a stringified payload and try to parse it so we
// can render friendly messages instead of falling back to generic errors.
if (typeof chatError === 'string') {
try {
chatError = JSON.parse(chatError);
} catch {
// If parsing fails, fall back to undefined so callers use their
// existing generic handling.
return undefined;
}
}
if (!isForwardedChatError(chatError)) {
return undefined;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import assert from 'assert';
import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js';
import { getChatErrorDetailsFromMeta } from '../../common/chatErrorMessages.js';
import type { ErrorInfo } from '../../../../../platform/agentHost/common/state/protocol/state.js';

function errorInfo(meta: Record<string, unknown> | undefined): ErrorInfo {
return { errorType: 'e', message: 'm', _meta: meta };
}

suite('ChatErrorMessages (malformed stringified payload)', () => {
ensureNoDisposablesAreLeakedInTestSuite();

test('invalid JSON in forwarded chatError returns undefined (safe fallback)', () => {
// Malformed JSON string should not throw; the helper should return undefined
// so callers fall back to generic error handling.
const payload = '{ fetchError: { type: "rateLimited", retryAfter: 60 }'; // missing closing brace
const details = getChatErrorDetailsFromMeta(errorInfo({ chatError: payload }));
assert.strictEqual(details, undefined);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import assert from 'assert';
import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js';
import { ChatFetchResponseType, getChatErrorDetailsFromMeta } from '../../common/chatErrorMessages.js';
import { ChatErrorLevel } from '../../common/chatService/chatService.js';
import type { ErrorInfo } from '../../../../../platform/agentHost/common/state/protocol/state.js';

function errorInfo(meta: Record<string, unknown> | undefined): ErrorInfo {
return { errorType: 'e', message: 'm', _meta: meta };
}

suite('ChatErrorMessages (stringified payload)', () => {
ensureNoDisposablesAreLeakedInTestSuite();

test('parses stringified forwarded chatError', () => {
const payload = JSON.stringify({
fetchError: {
type: ChatFetchResponseType.RateLimited,
retryAfter: 60,
capiError: { code: 'user_global_rate_limited', message: 'slow down' },
},
copilotPlan: 'free',
});

const details = getChatErrorDetailsFromMeta(errorInfo({ chatError: payload }));
assert.deepStrictEqual(details, {
code: ChatFetchResponseType.RateLimited,
message: 'You\'ve hit your session rate limit. Please upgrade your plan or wait 60 seconds for your limit to reset. [Learn More](https://aka.ms/github-copilot-rate-limit-error)',
level: ChatErrorLevel.Info,
isRateLimited: true,
});
});
});