diff --git a/client/src/components/Messages/Content/Error.tsx b/client/src/components/Messages/Content/Error.tsx
index d282fd399e..ccd9f305ac 100644
--- a/client/src/components/Messages/Content/Error.tsx
+++ b/client/src/components/Messages/Content/Error.tsx
@@ -58,6 +58,22 @@ type TGenericError = {
info: string;
};
+type TContextOverflow = {
+ info?: string;
+ provider?: string;
+ projectedMessageTokens?: number;
+ availableMessageTokens?: number;
+};
+
+/**
+ * SDK boilerplate already covered by the localized headline; whatever remains
+ * (specific guidance and the token budget breakdown) renders as details.
+ */
+const emptyMessagesBoilerplate = [
+ 'Message pruning removed all messages as none fit in the context window.',
+ 'Please increase the context window size or make your message shorter.',
+];
+
const errorMessages = {
[ErrorTypes.MODERATION]: 'com_error_moderation',
[ErrorTypes.NO_USER_KEY]: 'com_error_no_user_key',
@@ -102,6 +118,39 @@ const errorMessages = {
[ErrorTypes.STREAM_EXPIRED]: 'com_error_stream_expired',
[ErrorTypes.MODEL_NOT_FOUND]: langChainErrorKeys.MODEL_NOT_FOUND,
[ErrorTypes.MODEL_RATE_LIMIT]: langChainErrorKeys.MODEL_RATE_LIMIT,
+ [ErrorTypes.EMPTY_MESSAGES]: (json: TGenericError, localize: LocalizeFunction) => {
+ const detail = emptyMessagesBoilerplate
+ .reduce((info, sentence) => info.replace(sentence, ''), json.info ?? '')
+ .trim();
+ return (
+ <>
+ {localize('com_error_empty_messages')}
+ {detail && (
+ <>
+
+
+
+ >
+ )}
+ >
+ );
+ },
+ [ErrorTypes.FINAL_CONTEXT_OVERFLOW]: (json: TContextOverflow, localize: LocalizeFunction) => {
+ const { projectedMessageTokens: projected, availableMessageTokens: available } = json;
+ const message = localize('com_error_final_context_overflow');
+ if (typeof projected !== 'number' || typeof available !== 'number') {
+ return message;
+ }
+ return `${message} ${localize('com_error_context_tokens_detail', {
+ 0: projected,
+ 1: available,
+ })}`;
+ },
[ViolationTypes.BAN]:
'Your account has been temporarily banned due to violations of our service.',
[ViolationTypes.ILLEGAL_MODEL_REQUEST]: (json: TGenericError, localize: LocalizeFunction) => {
diff --git a/client/src/components/Messages/Content/__tests__/Error.spec.tsx b/client/src/components/Messages/Content/__tests__/Error.spec.tsx
index 3c6e3069dc..5a638e9cba 100644
--- a/client/src/components/Messages/Content/__tests__/Error.spec.tsx
+++ b/client/src/components/Messages/Content/__tests__/Error.spec.tsx
@@ -11,8 +11,16 @@ import Error from '../Error';
jest.mock('~/hooks', () => ({
useLocalize:
() =>
- (key: string): string =>
- (jest.requireActual('~/locales/en/translation.json') as Record)[key] ?? key,
+ (key: string, values?: Record): string => {
+ const template =
+ (jest.requireActual('~/locales/en/translation.json') as Record)[key] ?? key;
+ if (!values) {
+ return template;
+ }
+ return template.replace(/\{\{(\w+)\}\}/g, (match, name) =>
+ values[name] != null ? String(values[name]) : match,
+ );
+ },
}));
const catalog = translation as Record;
@@ -69,3 +77,56 @@ describe('Error — typed provider errors', () => {
).toBeInTheDocument();
});
});
+
+describe('Error — agent context budget errors', () => {
+ beforeAll(() => {
+ /** CodeBlock observes its code bar; jsdom ships no IntersectionObserver. */
+ (global as { IntersectionObserver?: unknown }).IntersectionObserver = class {
+ observe() {}
+ unobserve() {}
+ disconnect() {}
+ };
+ });
+
+ it('renders localized copy and the token breakdown for empty_messages', () => {
+ /** Mirrors the SDK payload as embedded by the agents controller's error prefix. */
+ const info =
+ 'Message pruning removed all messages as none fit in the context window. Please increase the context window size or make your message shorter.\nToken budget breakdown:\n maxContextTokens: 10\n messageTokens: 32 (4 messages)\n availableForMessages: 9';
+ const payload = `An error occurred while processing the request: ${JSON.stringify({
+ type: ErrorTypes.EMPTY_MESSAGES,
+ info,
+ })}`;
+ render();
+
+ expect(screen.getByText(catalog.com_error_empty_messages)).toBeInTheDocument();
+ expect(screen.getByText(/Token budget breakdown/)).toBeInTheDocument();
+ expect(screen.queryByText(/Something went wrong/)).not.toBeInTheDocument();
+ });
+
+ it('renders localized copy with token counts for final_context_overflow', () => {
+ const payload = `An error occurred while processing the request: ${JSON.stringify({
+ type: ErrorTypes.FINAL_CONTEXT_OVERFLOW,
+ info: 'Provider message formatting exceeded the context budget and no safe synthetic-context compaction could make it fit.',
+ provider: 'openAI',
+ projectedMessageTokens: 3,
+ availableMessageTokens: 0,
+ })}`;
+ render();
+
+ expect(
+ screen.getByText(new RegExp(catalog.com_error_final_context_overflow.slice(0, 40))),
+ ).toBeInTheDocument();
+ expect(screen.getByText(/need 3 tokens, but only 0 are available/)).toBeInTheDocument();
+ expect(screen.queryByText(/Something went wrong/)).not.toBeInTheDocument();
+ });
+
+ it('falls back to the plain sentence when overflow token counts are absent', () => {
+ const payload = JSON.stringify({
+ type: ErrorTypes.FINAL_CONTEXT_OVERFLOW,
+ info: 'Fallback provider message formatting exceeded the context budget before invocation.',
+ });
+ render();
+
+ expect(screen.getByText(catalog.com_error_final_context_overflow)).toBeInTheDocument();
+ });
+});
diff --git a/client/src/locales/en/translation.json b/client/src/locales/en/translation.json
index f293462a4b..15cea1e1b3 100644
--- a/client/src/locales/en/translation.json
+++ b/client/src/locales/en/translation.json
@@ -378,6 +378,8 @@
"com_error_expired_user_key": "Provided key for {{0}} expired at {{1}}. Please provide a new key and try again.",
"com_error_files_dupe": "Duplicate file detected.",
"com_error_files_empty": "Empty files are not allowed.",
+ "com_error_context_tokens_detail": "Your messages need {{0}} tokens, but only {{1}} are available.",
+ "com_error_empty_messages": "Your messages do not fit in the current context window, so there was nothing left to send to the model. Increase the max context size from the conversation parameters, shorten your message, or start a new conversation.",
"com_error_files_process": "An error occurred while processing the file.",
"com_error_files_skipped_dupe": "Skipped duplicate file(s): {{0}}",
"com_error_files_skipped_size": "Skipped file(s) over the {{0}} MB size limit: {{1}}",
@@ -386,6 +388,7 @@
"com_error_files_upload_canceled": "The file upload request was canceled. Note: the file upload may still be processing and will need to be manually deleted.",
"com_error_files_upload_too_large": "The file is too large. Please upload a file smaller than {{0}} MB",
"com_error_files_validation": "An error occurred while validating the file.",
+ "com_error_final_context_overflow": "The request exceeds the current context window and could not be reduced to fit. Increase the max context size from the conversation parameters or shorten your message.",
"com_error_google_tool_conflict": "Usage of built-in Google tools are not supported with external tools. Please disable either the built-in tools or the external tools.",
"com_error_google_video_unprocessable": "The linked video could not be processed. It is most likely too long for this model, but it may also be unavailable in this region or restricted. Try a shorter video, or describe the relevant parts in your message instead.",
"com_error_heic_conversion": "Failed to convert HEIC image to JPEG. Please try converting the image manually or use a different format.",
diff --git a/packages/data-provider/src/config.ts b/packages/data-provider/src/config.ts
index 2a01bba5d2..cb8aff0668 100644
--- a/packages/data-provider/src/config.ts
+++ b/packages/data-provider/src/config.ts
@@ -3307,6 +3307,14 @@ export enum ErrorTypes {
* Provider throttled or refused the request for exceeding a rate/spend allowance
*/
MODEL_RATE_LIMIT = 'model_rate_limit',
+ /**
+ * Context pruning removed every message; nothing fits the configured context window
+ */
+ EMPTY_MESSAGES = 'empty_messages',
+ /**
+ * Formatted provider payload exceeded the context budget before invocation
+ */
+ FINAL_CONTEXT_OVERFLOW = 'final_context_overflow',
}
/**