diff --git a/.changeset/fix-uri-length-lsp-crash.md b/.changeset/fix-uri-length-lsp-crash.md new file mode 100644 index 0000000000..e177298dc2 --- /dev/null +++ b/.changeset/fix-uri-length-lsp-crash.md @@ -0,0 +1,5 @@ +--- +"roo-cline": patch +--- + +Fix C# LSP crashes caused by excessively long URIs in diff views and checkpoints. Added URI length validation to prevent crashes when working with large files by truncating content that would exceed safe URI length limits. diff --git a/src/core/checkpoints/index.ts b/src/core/checkpoints/index.ts index dcbe796eb7..62c31a212e 100644 --- a/src/core/checkpoints/index.ts +++ b/src/core/checkpoints/index.ts @@ -12,6 +12,46 @@ import { getApiMetrics } from "../../shared/getApiMetrics" import { DIFF_VIEW_URI_SCHEME } from "../../integrations/editor/DiffViewProvider" +// Maximum safe URI length to avoid crashes in language servers +// Most systems have limits between 2KB-32KB, using conservative 8KB limit +const MAX_SAFE_URI_LENGTH = 8192 + +/** + * Safely creates a diff URI by validating the total URI length. + * If the URI would be too long, truncates the content to avoid LSP crashes. + */ +function createSafeDiffUri(fileName: string, content: string): vscode.Uri { + try { + const base64Content = Buffer.from(content).toString("base64") + const baseUri = `${DIFF_VIEW_URI_SCHEME}:${fileName}` + const testUri = vscode.Uri.parse(baseUri).with({ query: base64Content }).toString() + + if (testUri.length <= MAX_SAFE_URI_LENGTH) { + return vscode.Uri.parse(baseUri).with({ query: base64Content }) + } + + // Calculate available space for content after accounting for URI overhead + const overhead = baseUri.length + 50 // Extra buffer for URI encoding + const maxBase64Length = Math.max(0, MAX_SAFE_URI_LENGTH - overhead) + + // Truncate content to fit within safe URI length + const maxContentLength = Math.floor((maxBase64Length * 3) / 4) // Base64 is ~4/3 the size + const truncatedContent = + content.length > maxContentLength + ? content.substring(0, maxContentLength) + "\n... [Content truncated to prevent LSP crashes]" + : content + + const truncatedBase64 = Buffer.from(truncatedContent).toString("base64") + return vscode.Uri.parse(baseUri).with({ query: truncatedBase64 }) + } catch (error) { + console.error(`Failed to create diff URI for ${fileName}:`, error) + // Fallback to empty content if all else fails + return vscode.Uri.parse(`${DIFF_VIEW_URI_SCHEME}:${fileName}`).with({ + query: Buffer.from("").toString("base64"), + }) + } +} + import { CheckpointServiceOptions, RepoPerTaskCheckpointService } from "../../services/checkpoints" export function getCheckpointService(cline: Task) { @@ -277,12 +317,8 @@ export async function checkpointDiff(cline: Task, { ts, previousCommitHash, comm mode === "full" ? "Changes since task started" : "Changes since previous checkpoint", changes.map((change) => [ vscode.Uri.file(change.paths.absolute), - vscode.Uri.parse(`${DIFF_VIEW_URI_SCHEME}:${change.paths.relative}`).with({ - query: Buffer.from(change.content.before ?? "").toString("base64"), - }), - vscode.Uri.parse(`${DIFF_VIEW_URI_SCHEME}:${change.paths.relative}`).with({ - query: Buffer.from(change.content.after ?? "").toString("base64"), - }), + createSafeDiffUri(change.paths.relative, change.content.before ?? ""), + createSafeDiffUri(change.paths.relative, change.content.after ?? ""), ]), ) } catch (err) { diff --git a/src/integrations/editor/DiffViewProvider.ts b/src/integrations/editor/DiffViewProvider.ts index 3ab0419618..8ffba4fd9a 100644 --- a/src/integrations/editor/DiffViewProvider.ts +++ b/src/integrations/editor/DiffViewProvider.ts @@ -16,6 +16,46 @@ import { DecorationController } from "./DecorationController" export const DIFF_VIEW_URI_SCHEME = "cline-diff" +// Maximum safe URI length to avoid crashes in language servers +// Most systems have limits between 2KB-32KB, using conservative 8KB limit +const MAX_SAFE_URI_LENGTH = 8192 + +/** + * Safely creates a diff URI by validating the total URI length. + * If the URI would be too long, truncates the content to avoid LSP crashes. + */ +function createSafeDiffUri(fileName: string, content: string): vscode.Uri { + try { + const base64Content = Buffer.from(content).toString("base64") + const baseUri = `${DIFF_VIEW_URI_SCHEME}:${fileName}` + const testUri = vscode.Uri.parse(baseUri).with({ query: base64Content }).toString() + + if (testUri.length <= MAX_SAFE_URI_LENGTH) { + return vscode.Uri.parse(baseUri).with({ query: base64Content }) + } + + // Calculate available space for content after accounting for URI overhead + const overhead = baseUri.length + 50 // Extra buffer for URI encoding + const maxBase64Length = Math.max(0, MAX_SAFE_URI_LENGTH - overhead) + + // Truncate content to fit within safe URI length + const maxContentLength = Math.floor((maxBase64Length * 3) / 4) // Base64 is ~4/3 the size + const truncatedContent = + content.length > maxContentLength + ? content.substring(0, maxContentLength) + "\n... [Content truncated to prevent LSP crashes]" + : content + + const truncatedBase64 = Buffer.from(truncatedContent).toString("base64") + return vscode.Uri.parse(baseUri).with({ query: truncatedBase64 }) + } catch (error) { + console.error(`Failed to create diff URI for ${fileName}:`, error) + // Fallback to empty content if all else fails + return vscode.Uri.parse(`${DIFF_VIEW_URI_SCHEME}:${fileName}`).with({ + query: Buffer.from("").toString("base64"), + }) + } +} + // TODO: https://github.com/cline/cline/pull/3354 export class DiffViewProvider { // Properties to store the results of saveChanges @@ -310,14 +350,14 @@ export class DiffViewProvider { indentBy: "", suppressEmptyNode: true, processEntities: false, - tagValueProcessor: (name, value) => { + tagValueProcessor: (_name, value) => { if (typeof value === "string") { // Only escape <, >, and & characters return value.replace(/&/g, "&").replace(//g, ">") } return value }, - attributeValueProcessor: (name, value) => { + attributeValueProcessor: (_name, value) => { if (typeof value === "string") { // Only escape <, >, and & characters return value.replace(/&/g, "&").replace(//g, ">") @@ -444,9 +484,7 @@ export class DiffViewProvider { vscode.commands.executeCommand( "vscode.diff", - vscode.Uri.parse(`${DIFF_VIEW_URI_SCHEME}:${fileName}`).with({ - query: Buffer.from(this.originalContent ?? "").toString("base64"), - }), + createSafeDiffUri(fileName, this.originalContent ?? ""), uri, `${fileName}: ${fileExists ? "Original ↔ Roo's Changes" : "New File"} (Editable)`, { preserveFocus: true },