Skip to content

Comments

Fixes #911: handle prompt too long errors#920

Open
hemant838 wants to merge 1 commit intosourcebot-dev:mainfrom
hemant838:hemant9971/fix-prompt-too-long-error
Open

Fixes #911: handle prompt too long errors#920
hemant838 wants to merge 1 commit intosourcebot-dev:mainfrom
hemant838:hemant9971/fix-prompt-too-long-error

Conversation

@hemant838
Copy link

@hemant838 hemant838 commented Feb 21, 2026

fix(chat): handle prompt too long errors by truncating files and limiting history

Prevent context window overflow by truncating large file contents (default 100K chars) and capping message history (default 50 messages). When a context window error still occurs, detect provider-specific error messages and show a user-friendly message with actionable guidance.

Summary by CodeRabbit

  • New Features

    • Files are now automatically truncated when exceeding configured character limits. Users are notified when truncation occurs and can retrieve full content using dedicated tools.
    • Enhanced error detection and messaging for context window limit scenarios with clearer user feedback.
  • Improvements

    • Message history is intelligently trimmed to maintain conversation size within configured limits.

…ting history

Prevent context window overflow by truncating large file contents (default 100K chars) and capping message history (default 50 messages). When a context window error still occurs, detect provider-specific error messages and show a user-friendly message with actionable guidance.

Fixes sourcebot-dev#911

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 21, 2026

Walkthrough

This PR implements context window management for the chat system by adding file content truncation, message history limiting, and context window error detection. Two new environment variables configure maximum file character limits and message history size, with corresponding logic applied throughout the chat pipeline and user-facing error messaging.

Changes

Cohort / File(s) Summary
Configuration
packages/shared/src/env.server.ts
Added two new environment variables: SOURCEBOT_CHAT_FILE_MAX_CHARACTERS (default 100,000) and SOURCEBOT_CHAT_MAX_MESSAGE_HISTORY (default 50).
Chat Utilities & Constants
packages/web/src/features/chat/utils.ts
Added truncateFileContent() function for limiting file content size with truncation notices, isContextWindowError() for detecting context window errors via pattern matching, and CONTEXT_WINDOW_USER_MESSAGE constant for user-facing error text.
Core Chat Flow
packages/web/src/app/api/(server)/chat/route.ts, packages/web/src/features/chat/agent.ts, packages/web/src/features/chat/tools.ts
Integrated message history trimming in API route, file truncation with filesWereTruncated flag propagation in agent, context window error normalization, and applied truncation to readFilesTool output.
UI & Error Handling
packages/web/src/features/chat/components/chatThread/errorBanner.tsx
Updated error banner to display "Context limit exceeded" message for context window errors instead of generic error text.
Testing
packages/web/src/features/chat/utils.test.ts
Added comprehensive test coverage for new utility functions including truncation behavior (under/at/over limit with line-boundary handling) and context window error detection patterns.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • fix(web): Fix issue with blocking chat api #907: Modifies the same chat API route (packages/web/src/app/api/(server)/chat/route.ts) with potentially overlapping changes to the POST flow, including chatId/captureEvent handling and message stream creation.

Suggested reviewers

  • msukkari
  • brendan-kellam
🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title directly addresses the main objective of the PR—handling prompt too long errors—and accurately reflects the primary changes across multiple files for context window error handling.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (3)
packages/web/src/app/api/(server)/chat/route.ts (1)

109-128: Context-window errors are routed to Sentry before detection

logger.error and Sentry.captureException fire unconditionally, so expected context-window errors (user-triggered, not bugs) generate Sentry noise. Move the context-window check before the capture.

♻️ Proposed reorder
  onError: (error: unknown) => {
-     logger.error(error);
-     Sentry.captureException(error);
-
      if (error == null) {
          return 'unknown error';
      }

      const errorMessage = (() => {
          if (typeof error === 'string') return error;
          if (error instanceof Error) return error.message;
          return JSON.stringify(error);
      })();

      if (isContextWindowError(errorMessage)) {
          return CONTEXT_WINDOW_USER_MESSAGE;
      }

+     logger.error(error);
+     Sentry.captureException(error);
      return errorMessage;
  }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/web/src/app/api/`(server)/chat/route.ts around lines 109 - 128, The
onError handler currently calls logger.error and Sentry.captureException before
checking for context-window errors, causing expected user-triggered
context-window issues to be sent to Sentry; change the flow in the onError
function to first derive the errorMessage (compute string from error using the
existing typeof/instanceof/JSON.stringify logic), then call
isContextWindowError(errorMessage) and return CONTEXT_WINDOW_USER_MESSAGE
immediately if true, and only after that call logger.error(error) and
Sentry.captureException(error) and return the errorMessage for other errors;
reference the onError handler, the error-to-string logic, isContextWindowError,
CONTEXT_WINDOW_USER_MESSAGE, logger.error and Sentry.captureException when
making the reorder.
packages/web/src/features/chat/tools.ts (1)

127-136: Truncation notice inherits a line number from addLineNumbers

truncateFileContent appends \n\n... [truncated: showing X of Y lines] to the content. When addLineNumbers(content) is then applied, that notice line itself gets a line number (e.g., 52:... [truncated: showing 51 of 100 lines]), which is a bit misleading. The same pattern occurs in agent.ts (Line 209).

Consider placing the truncation notice outside the numbered block by applying addLineNumbers before appending the notice inside truncateFileContent, or appending the notice after the addLineNumbers call:

♻️ Suggested approach in the callers
- const { content } = truncateFileContent(response.source, env.SOURCEBOT_CHAT_FILE_MAX_CHARACTERS);
+ const { content, wasTruncated, notice } = truncateFileContent(response.source, env.SOURCEBOT_CHAT_FILE_MAX_CHARACTERS);
  return {
      ...
-     source: addLineNumbers(content),
+     source: addLineNumbers(wasTruncated ? content.slice(0, content.lastIndexOf('\n\n...')) : content)
+         + (wasTruncated ? notice : ''),
      ...
  };

Alternatively, refactor truncateFileContent to return { body, notice } separately, so callers can choose where to place the notice relative to line-numbered output.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/web/src/features/chat/tools.ts` around lines 127 - 136, The
truncation notice is being numbered because callers (map in this file and
agent.ts) call addLineNumbers(content) after truncateFileContent adds the
"\n\n... [truncated...]" line; fix by either (A) applying addLineNumbers to the
truncated body first and appending the truncation notice afterwards in this
mapper (i.e., call truncateFileContent(response.source) -> use returned body
with addLineNumbers(body) then concat the notice string without numbering), or
(B) refactor truncateFileContent to return an object like { body, notice } so
callers such as the mapper in packages/web/src/features/chat/tools.ts and the
function in agent.ts can call addLineNumbers(body) and then append notice
outside the numbered block; update uses of truncateFileContent and preserve
existing behaviour of FileSourceResponse mapping (path, repo, language, source,
revision).
packages/web/src/features/chat/utils.ts (1)

200-211: /token.?limit/i and /max_tokens/i can produce false positives

/token.?limit/i matches phrases like "You've exceeded the token limit for your subscription tier" (a billing/rate-limit error, not a context-window error). /max_tokens/i matches "Increase max_tokens to see the full response" (a configuration note). Both could cause a context-window message to be displayed for unrelated errors.

♻️ Tighten the patterns
- /token.?limit/i,
- /max_tokens/i,
+ /context.?token.?limit/i,
+ /max_tokens.*exceeded/i,

Or simply remove them if the remaining patterns already cover the real providers you need.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/web/src/features/chat/utils.ts` around lines 200 - 211, The patterns
`/token.?limit/i` and `/max_tokens/i` in CONTEXT_WINDOW_ERROR_PATTERNS are too
broad and cause false positives (billing/configuration messages flagged as
context-window errors); fix by either removing these two entries from the
CONTEXT_WINDOW_ERROR_PATTERNS array or tightening them to more specific regexes
that only match true context-window messages (e.g., replace `/token.?limit/i`
with a stricter pattern like `/exceeds? (the )?maximum token(?:s)? for (?:the
)?context/i` and change `/max_tokens/i` to `\bmax[_-]?tokens\b` only if you need
config-key matches), and ensure CONTEXT_WINDOW_ERROR_PATTERNS still covers
provider-specific phrases used elsewhere in the code (refer to the
CONTEXT_WINDOW_ERROR_PATTERNS constant to apply the change).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/shared/src/env.server.ts`:
- Around line 234-235: The env schema allows zero/negative values because
numberSchema (z.coerce.number()) has no minimum, causing slice(-0)/slice(0) and
full truncation edge-cases; update the SOURCEBOT_CHAT_MAX_MESSAGE_HISTORY and
SOURCEBOT_CHAT_FILE_MAX_CHARACTERS entries to enforce a sensible lower bound
(e.g., add .min(1) to numberSchema before .default or use
numberSchema.min(1).default(...)) so invalid 0/negative values are rejected, or
alternatively add an explicit guard in the chat trimming logic (in route.ts) to
treat <=0 as 1 or skip trimming; update the schema entries
SOURCEBOT_CHAT_MAX_MESSAGE_HISTORY and SOURCEBOT_CHAT_FILE_MAX_CHARACTERS
accordingly.

In `@packages/web/src/app/api/`(server)/chat/route.ts:
- Around line 208-211: The current trim uses slice(-maxMessages) which can start
with an assistant message and break providers that require the first turn to be
"user"; change the trimming logic around messageHistory, maxMessages and
trimmedMessageHistory so you compute a start index = messageHistory.length -
maxMessages, then if messageHistory[start].role === "assistant" increment start
by 1 (to ensure the first retained turn is a user) before slicing; keep other
behavior the same when messageHistory.length <= maxMessages.

In `@packages/web/src/features/chat/utils.test.ts`:
- Around line 373-374: Update the stale inline comment to match the actual test
limit: change the comment that currently reads "Limit of 20 characters" to
reflect the 15-character limit used in the test call to
truncateFileContent(source, 15), ensuring the comment accurately describes the
expected behavior for truncateFileContent in this test.

---

Nitpick comments:
In `@packages/web/src/app/api/`(server)/chat/route.ts:
- Around line 109-128: The onError handler currently calls logger.error and
Sentry.captureException before checking for context-window errors, causing
expected user-triggered context-window issues to be sent to Sentry; change the
flow in the onError function to first derive the errorMessage (compute string
from error using the existing typeof/instanceof/JSON.stringify logic), then call
isContextWindowError(errorMessage) and return CONTEXT_WINDOW_USER_MESSAGE
immediately if true, and only after that call logger.error(error) and
Sentry.captureException(error) and return the errorMessage for other errors;
reference the onError handler, the error-to-string logic, isContextWindowError,
CONTEXT_WINDOW_USER_MESSAGE, logger.error and Sentry.captureException when
making the reorder.

In `@packages/web/src/features/chat/tools.ts`:
- Around line 127-136: The truncation notice is being numbered because callers
(map in this file and agent.ts) call addLineNumbers(content) after
truncateFileContent adds the "\n\n... [truncated...]" line; fix by either (A)
applying addLineNumbers to the truncated body first and appending the truncation
notice afterwards in this mapper (i.e., call
truncateFileContent(response.source) -> use returned body with
addLineNumbers(body) then concat the notice string without numbering), or (B)
refactor truncateFileContent to return an object like { body, notice } so
callers such as the mapper in packages/web/src/features/chat/tools.ts and the
function in agent.ts can call addLineNumbers(body) and then append notice
outside the numbered block; update uses of truncateFileContent and preserve
existing behaviour of FileSourceResponse mapping (path, repo, language, source,
revision).

In `@packages/web/src/features/chat/utils.ts`:
- Around line 200-211: The patterns `/token.?limit/i` and `/max_tokens/i` in
CONTEXT_WINDOW_ERROR_PATTERNS are too broad and cause false positives
(billing/configuration messages flagged as context-window errors); fix by either
removing these two entries from the CONTEXT_WINDOW_ERROR_PATTERNS array or
tightening them to more specific regexes that only match true context-window
messages (e.g., replace `/token.?limit/i` with a stricter pattern like
`/exceeds? (the )?maximum token(?:s)? for (?:the )?context/i` and change
`/max_tokens/i` to `\bmax[_-]?tokens\b` only if you need config-key matches),
and ensure CONTEXT_WINDOW_ERROR_PATTERNS still covers provider-specific phrases
used elsewhere in the code (refer to the CONTEXT_WINDOW_ERROR_PATTERNS constant
to apply the change).

Comment on lines +234 to +235
SOURCEBOT_CHAT_FILE_MAX_CHARACTERS: numberSchema.default(100_000),
SOURCEBOT_CHAT_MAX_MESSAGE_HISTORY: numberSchema.default(50),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Missing lower-bound validation; SOURCEBOT_CHAT_MAX_MESSAGE_HISTORY=0 silently disables trimming

numberSchema is z.coerce.number() with no minimum. For SOURCEBOT_CHAT_MAX_MESSAGE_HISTORY, a value of 0 produces a JavaScript gotcha: Array.prototype.slice(-0) is identical to slice(0), returning the full array — so history trimming is silently skipped rather than capping to zero messages. Negative values produce similarly unexpected results.

For SOURCEBOT_CHAT_FILE_MAX_CHARACTERS, 0 would truncate every file to an empty body with just the notice.

🛡️ Proposed fix
- SOURCEBOT_CHAT_FILE_MAX_CHARACTERS: numberSchema.default(100_000),
- SOURCEBOT_CHAT_MAX_MESSAGE_HISTORY: numberSchema.default(50),
+ SOURCEBOT_CHAT_FILE_MAX_CHARACTERS: numberSchema.min(1).default(100_000),
+ SOURCEBOT_CHAT_MAX_MESSAGE_HISTORY: numberSchema.min(1).default(50),

Alternatively, guard against maxMessages <= 0 in route.ts before performing the slice.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
SOURCEBOT_CHAT_FILE_MAX_CHARACTERS: numberSchema.default(100_000),
SOURCEBOT_CHAT_MAX_MESSAGE_HISTORY: numberSchema.default(50),
SOURCEBOT_CHAT_FILE_MAX_CHARACTERS: numberSchema.min(1).default(100_000),
SOURCEBOT_CHAT_MAX_MESSAGE_HISTORY: numberSchema.min(1).default(50),
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/shared/src/env.server.ts` around lines 234 - 235, The env schema
allows zero/negative values because numberSchema (z.coerce.number()) has no
minimum, causing slice(-0)/slice(0) and full truncation edge-cases; update the
SOURCEBOT_CHAT_MAX_MESSAGE_HISTORY and SOURCEBOT_CHAT_FILE_MAX_CHARACTERS
entries to enforce a sensible lower bound (e.g., add .min(1) to numberSchema
before .default or use numberSchema.min(1).default(...)) so invalid 0/negative
values are rejected, or alternatively add an explicit guard in the chat trimming
logic (in route.ts) to treat <=0 as 1 or skip trimming; update the schema
entries SOURCEBOT_CHAT_MAX_MESSAGE_HISTORY and
SOURCEBOT_CHAT_FILE_MAX_CHARACTERS accordingly.

Comment on lines +208 to +211
const maxMessages = env.SOURCEBOT_CHAT_MAX_MESSAGE_HISTORY;
const trimmedMessageHistory = messageHistory.length > maxMessages
? messageHistory.slice(-maxMessages)
: messageHistory;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

slice(-maxMessages) can produce a history that starts with an assistant message

messageHistory follows the pattern [user₁, assistant₁, user₂, assistant₂, …, userₙ]. When messageHistory.length > maxMessages and maxMessages is even (e.g. 50), slice(-50) drops the oldest user message and the resulting history begins with an orphaned assistant turn. Providers like Anthropic's Messages API require the first turn to be "user" and will reject the request with an error when this constraint is violated.

🐛 Proposed fix — ensure trimmed history always starts with a user message
  const maxMessages = env.SOURCEBOT_CHAT_MAX_MESSAGE_HISTORY;
- const trimmedMessageHistory = messageHistory.length > maxMessages
-     ? messageHistory.slice(-maxMessages)
-     : messageHistory;
+ let trimmedMessageHistory = messageHistory.length > maxMessages
+     ? messageHistory.slice(-maxMessages)
+     : messageHistory;
+ // Providers (e.g., Anthropic) require the first message to be from the user.
+ // If trimming produced an assistant-first sequence, drop the leading assistant turn.
+ if (trimmedMessageHistory.length > 0 && trimmedMessageHistory[0].role === 'assistant') {
+     trimmedMessageHistory = trimmedMessageHistory.slice(1);
+ }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/web/src/app/api/`(server)/chat/route.ts around lines 208 - 211, The
current trim uses slice(-maxMessages) which can start with an assistant message
and break providers that require the first turn to be "user"; change the
trimming logic around messageHistory, maxMessages and trimmedMessageHistory so
you compute a start index = messageHistory.length - maxMessages, then if
messageHistory[start].role === "assistant" increment start by 1 (to ensure the
first retained turn is a user) before slicing; keep other behavior the same when
messageHistory.length <= maxMessages.

Comment on lines +373 to +374
// Limit of 20 characters: "line 1\nline 2\nline 3" is 20 chars
const result = truncateFileContent(source, 15);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Stale comment — limit is 15, not 20

The inline comment says "Limit of 20 characters" but the call uses truncateFileContent(source, 15). The string 'line 1\nline 2\nline 3' is indeed 20 chars (describing the full third-line boundary), but the test limit is 15. The comment is misleading.

📝 Suggested fix
-    // Limit of 20 characters: "line 1\nline 2\nline 3" is 20 chars
+    // Limit of 15 characters: last newline before index 15 is at index 13 ("line 1\nline 2")
     const result = truncateFileContent(source, 15);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// Limit of 20 characters: "line 1\nline 2\nline 3" is 20 chars
const result = truncateFileContent(source, 15);
// Limit of 15 characters: last newline before index 15 is at index 13 ("line 1\nline 2")
const result = truncateFileContent(source, 15);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/web/src/features/chat/utils.test.ts` around lines 373 - 374, Update
the stale inline comment to match the actual test limit: change the comment that
currently reads "Limit of 20 characters" to reflect the 15-character limit used
in the test call to truncateFileContent(source, 15), ensuring the comment
accurately describes the expected behavior for truncateFileContent in this test.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant