diff --git a/src/core/mentions/index.ts b/src/core/mentions/index.ts index d0d305d096..881c2cc292 100644 --- a/src/core/mentions/index.ts +++ b/src/core/mentions/index.ts @@ -267,11 +267,18 @@ async function getFileOrFolderContent( const absoluteFilePath = path.resolve(absPath, entry.name) fileContentPromises.push( (async () => { + let isBinary = false + try { + isBinary = await isBinaryFile(absoluteFilePath) + } catch (error) { + // If isBinaryFile throws an error (e.g., RangeError), treat as binary + console.warn(`Error checking if file is binary for ${absoluteFilePath}:`, error) + isBinary = true + } + if (isBinary) { + return undefined + } try { - const isBinary = await isBinaryFile(absoluteFilePath).catch(() => false) - if (isBinary) { - return undefined - } const content = await extractTextFromFile(absoluteFilePath, maxReadFileLine) return `\n${content}\n` } catch (error) { diff --git a/src/core/tools/__tests__/readFileTool.spec.ts b/src/core/tools/__tests__/readFileTool.spec.ts index 44be1d3b92..ab7e69eee7 100644 --- a/src/core/tools/__tests__/readFileTool.spec.ts +++ b/src/core/tools/__tests__/readFileTool.spec.ts @@ -518,5 +518,26 @@ describe("read_file tool XML output structure", () => { `\n${testFilePath}Access to ${testFilePath} is blocked by the .rooignore file settings. You must try to continue in the task without using this file, or ask the user to update the .rooignore file.\n`, ) }) + + it("should handle RangeError from isBinaryFile gracefully", async () => { + // Setup - mock isBinaryFile to throw RangeError + mockedIsBinaryFile.mockRejectedValue(new RangeError("Invalid array length")) + mockedCountFileLines.mockResolvedValue(5) + + // Execute - the main goal is to verify the error doesn't crash the application + const result = await executeReadFileTool( + {}, + { + totalLines: 5, + }, + ) + + // Verify that the file is processed (the error is handled gracefully) + expect(result).toContain(`${testFilePath}`) + + // Verify that we get a valid XML response (not an error) + expect(result).toMatch(/.*<\/files>/s) + expect(result).not.toContain("") + }) }) }) diff --git a/src/core/tools/readFileTool.ts b/src/core/tools/readFileTool.ts index 6de8dd5642..b8d3f8989c 100644 --- a/src/core/tools/readFileTool.ts +++ b/src/core/tools/readFileTool.ts @@ -433,7 +433,20 @@ export async function readFileTool( // Process approved files try { - const [totalLines, isBinary] = await Promise.all([countFileLines(fullPath), isBinaryFile(fullPath)]) + let totalLines: number + let isBinary: boolean + + // First, count the file lines (this should not fail for valid files) + totalLines = await countFileLines(fullPath) + + // Then check if it's binary, with error handling specific to isBinaryFile + try { + isBinary = await isBinaryFile(fullPath) + } catch (error) { + // If isBinaryFile throws an error (e.g., RangeError), treat the file as binary + console.warn(`Error checking if file is binary for ${relPath}:`, error) + isBinary = true + } // Handle binary files (but allow specific file types that extractTextFromFile can handle) if (isBinary) { diff --git a/src/integrations/misc/__tests__/extract-text-large-files.spec.ts b/src/integrations/misc/__tests__/extract-text-large-files.spec.ts index fc2f7f54b6..e164c070ab 100644 --- a/src/integrations/misc/__tests__/extract-text-large-files.spec.ts +++ b/src/integrations/misc/__tests__/extract-text-large-files.spec.ts @@ -25,6 +25,8 @@ describe("extractTextFromFile - Large File Handling", () => { // Set default mock behavior mockedFs.access.mockResolvedValue(undefined) mockedIsBinaryFile.mockResolvedValue(false) + // Mock console.warn + vi.spyOn(console, "warn").mockImplementation(() => {}) }) it("should truncate files that exceed maxReadFileLine limit", async () => { @@ -218,4 +220,20 @@ describe("extractTextFromFile - Large File Handling", () => { "File not found: /test/nonexistent.ts", ) }) + + it("should handle RangeError from isBinaryFile and treat file as binary", async () => { + // Setup - mock isBinaryFile to throw RangeError + mockedIsBinaryFile.mockRejectedValue(new RangeError("Invalid array length")) + + // Execute and expect it to throw since file is treated as binary + await expect(extractTextFromFile("/test/problematic-file.bin", 100)).rejects.toThrow( + "Cannot read text for file type: .bin", + ) + + // Verify that the warning was logged + expect(console.warn).toHaveBeenCalledWith( + "Error checking if file is binary for /test/problematic-file.bin:", + expect.any(RangeError), + ) + }) }) diff --git a/src/integrations/misc/extract-text.ts b/src/integrations/misc/extract-text.ts index 8231c609be..a2a9c722ee 100644 --- a/src/integrations/misc/extract-text.ts +++ b/src/integrations/misc/extract-text.ts @@ -86,7 +86,14 @@ export async function extractTextFromFile(filePath: string, maxReadFileLine?: nu } // Handle other files - const isBinary = await isBinaryFile(filePath).catch(() => false) + let isBinary = false + try { + isBinary = await isBinaryFile(filePath) + } catch (error) { + // If isBinaryFile throws an error (e.g., RangeError), treat as binary + console.warn(`Error checking if file is binary for ${filePath}:`, error) + isBinary = true + } if (!isBinary) { // Check if we need to apply line limit