Add clarity to error messages (#14879)

This commit is contained in:
Gaurav Sehgal
2025-12-13 06:31:12 +05:30
committed by GitHub
parent ad60cbfc2c
commit d9f94103cd
5 changed files with 88 additions and 54 deletions

View File

@@ -17,6 +17,7 @@ import {
COMMON_IGNORE_PATTERNS,
// DEFAULT_FILE_EXCLUDES,
} from '@google/gemini-cli-core';
import * as core from '@google/gemini-cli-core';
import * as os from 'node:os';
import { ToolCallStatus } from '../types.js';
import type { UseHistoryManagerReturn } from './useHistoryManager.js';
@@ -120,7 +121,6 @@ describe('handleAtCommand', () => {
expect(result).toEqual({
processedQuery: [{ text: query }],
shouldProceed: true,
});
});
@@ -138,7 +138,6 @@ describe('handleAtCommand', () => {
expect(result).toEqual({
processedQuery: [{ text: queryWithSpaces }],
shouldProceed: true,
});
expect(mockOnDebugMessage).toHaveBeenCalledWith(
'Lone @ detected, will be treated as text in the modified query.',
@@ -171,7 +170,6 @@ describe('handleAtCommand', () => {
{ text: fileContent },
{ text: '\n--- End of content ---' },
],
shouldProceed: true,
});
expect(mockAddItem).toHaveBeenCalledWith(
expect.objectContaining({
@@ -211,7 +209,6 @@ describe('handleAtCommand', () => {
{ text: fileContent },
{ text: '\n--- End of content ---' },
],
shouldProceed: true,
});
expect(mockOnDebugMessage).toHaveBeenCalledWith(
`Path ${dirPath} resolved to directory, using glob: ${resolvedGlob}`,
@@ -246,7 +243,6 @@ describe('handleAtCommand', () => {
{ text: fileContent },
{ text: '\n--- End of content ---' },
],
shouldProceed: true,
});
});
@@ -276,7 +272,6 @@ describe('handleAtCommand', () => {
{ text: fileContent },
{ text: '\n--- End of content ---' },
],
shouldProceed: true,
});
expect(mockAddItem).toHaveBeenCalledWith(
expect.objectContaining({
@@ -321,7 +316,6 @@ describe('handleAtCommand', () => {
{ text: content2 },
{ text: '\n--- End of content ---' },
],
shouldProceed: true,
});
});
@@ -362,7 +356,6 @@ describe('handleAtCommand', () => {
{ text: content2 },
{ text: '\n--- End of content ---' },
],
shouldProceed: true,
});
});
@@ -401,7 +394,6 @@ describe('handleAtCommand', () => {
{ text: content1 },
{ text: '\n--- End of content ---' },
],
shouldProceed: true,
});
expect(mockOnDebugMessage).toHaveBeenCalledWith(
`Path ${invalidFile} not found directly, attempting glob search.`,
@@ -428,7 +420,6 @@ describe('handleAtCommand', () => {
expect(result).toEqual({
processedQuery: [{ text: 'Check @nonexistent.txt and @ also' }],
shouldProceed: true,
});
});
@@ -462,7 +453,6 @@ describe('handleAtCommand', () => {
expect(result).toEqual({
processedQuery: [{ text: query }],
shouldProceed: true,
});
expect(mockOnDebugMessage).toHaveBeenCalledWith(
`Path ${gitIgnoredFile} is git-ignored and will be skipped.`,
@@ -501,7 +491,6 @@ describe('handleAtCommand', () => {
{ text: 'console.log("Hello world");' },
{ text: '\n--- End of content ---' },
],
shouldProceed: true,
});
});
@@ -534,7 +523,6 @@ describe('handleAtCommand', () => {
{ text: '# Project README' },
{ text: '\n--- End of content ---' },
],
shouldProceed: true,
});
expect(mockOnDebugMessage).toHaveBeenCalledWith(
`Path ${gitIgnoredFile} is git-ignored and will be skipped.`,
@@ -562,7 +550,6 @@ describe('handleAtCommand', () => {
expect(result).toEqual({
processedQuery: [{ text: query }],
shouldProceed: true,
});
expect(mockOnDebugMessage).toHaveBeenCalledWith(
`Path ${gitFile} is git-ignored and will be skipped.`,
@@ -595,7 +582,8 @@ describe('handleAtCommand', () => {
`Glob tool not found. Path ${invalidFile} will be skipped.`,
);
expect(result.processedQuery).toEqual([{ text: query }]);
expect(result.shouldProceed).toBe(true);
expect(result.processedQuery).not.toBeNull();
expect(result.error).toBeUndefined();
});
});
@@ -622,7 +610,6 @@ describe('handleAtCommand', () => {
expect(result).toEqual({
processedQuery: [{ text: query }],
shouldProceed: true,
});
expect(mockOnDebugMessage).toHaveBeenCalledWith(
`Path ${geminiIgnoredFile} is gemini-ignored and will be skipped.`,
@@ -660,7 +647,6 @@ describe('handleAtCommand', () => {
{ text: 'console.log("Hello world");' },
{ text: '\n--- End of content ---' },
],
shouldProceed: true,
});
});
@@ -696,7 +682,6 @@ describe('handleAtCommand', () => {
{ text: '// Main application entry' },
{ text: '\n--- End of content ---' },
],
shouldProceed: true,
});
expect(mockOnDebugMessage).toHaveBeenCalledWith(
`Path ${geminiIgnoredFile} is gemini-ignored and will be skipped.`,
@@ -824,7 +809,6 @@ describe('handleAtCommand', () => {
{ text: fileContent },
{ text: '\n--- End of content ---' },
],
shouldProceed: true,
});
},
);
@@ -863,7 +847,6 @@ describe('handleAtCommand', () => {
{ text: content2 },
{ text: '\n--- End of content ---' },
],
shouldProceed: true,
});
});
@@ -893,7 +876,6 @@ describe('handleAtCommand', () => {
{ text: fileContent },
{ text: '\n--- End of content ---' },
],
shouldProceed: true,
});
});
@@ -924,7 +906,6 @@ describe('handleAtCommand', () => {
{ text: fileContent },
{ text: '\n--- End of content ---' },
],
shouldProceed: true,
});
});
@@ -955,7 +936,6 @@ describe('handleAtCommand', () => {
{ text: fileContent },
{ text: '\n--- End of content ---' },
],
shouldProceed: true,
});
});
@@ -986,11 +966,10 @@ describe('handleAtCommand', () => {
{ text: fileContent },
{ text: '\n--- End of content ---' },
],
shouldProceed: true,
});
});
it('should not terminate at period within file name', async () => {
it('should correctly handle file paths with multiple periods', async () => {
const fileContent = 'Version info';
const filePath = await createTestFile(
path.join(testRootDir, 'version.1.2.3.txt'),
@@ -1017,7 +996,6 @@ describe('handleAtCommand', () => {
{ text: fileContent },
{ text: '\n--- End of content ---' },
],
shouldProceed: true,
});
});
@@ -1046,7 +1024,6 @@ describe('handleAtCommand', () => {
{ text: fileContent },
{ text: '\n--- End of content ---' },
],
shouldProceed: true,
});
});
@@ -1075,7 +1052,6 @@ describe('handleAtCommand', () => {
{ text: fileContent },
{ text: '\n--- End of content ---' },
],
shouldProceed: true,
});
});
@@ -1104,7 +1080,6 @@ describe('handleAtCommand', () => {
{ text: fileContent },
{ text: '\n--- End of content ---' },
],
shouldProceed: true,
});
});
});
@@ -1136,7 +1111,6 @@ describe('handleAtCommand', () => {
{ text: fileContent },
{ text: '\n--- End of content ---' },
],
shouldProceed: true,
});
expect(mockOnDebugMessage).toHaveBeenCalledWith(
@@ -1165,7 +1139,8 @@ describe('handleAtCommand', () => {
signal: abortController.signal,
});
expect(result.shouldProceed).toBe(true);
expect(result.processedQuery).not.toBeNull();
expect(result.error).toBeUndefined();
expect(result.processedQuery).toEqual(
expect.arrayContaining([
{ text: `Check @${path.join(subDirPath, '**')} please.` },
@@ -1207,7 +1182,6 @@ describe('handleAtCommand', () => {
expect(result).toEqual({
processedQuery: [{ text: `Check @${outsidePath} please.` }],
shouldProceed: true,
});
expect(mockOnDebugMessage).toHaveBeenCalledWith(
@@ -1326,7 +1300,8 @@ describe('handleAtCommand', () => {
signal: abortController.signal,
});
expect(result.shouldProceed).toBe(false);
expect(result.processedQuery).toBeNull();
expect(result.error).toBeDefined();
expect(mockAddItem).toHaveBeenCalledWith(
expect.objectContaining({
type: 'tool_group',
@@ -1342,4 +1317,51 @@ describe('handleAtCommand', () => {
);
});
});
it('should return error if the read_many_files tool is cancelled by user', async () => {
const fileContent = 'Some content';
const filePath = await createTestFile(
path.join(testRootDir, 'file.txt'),
fileContent,
);
const query = `@${filePath}`;
// Simulate user cancellation
const mockToolInstance = {
buildAndExecute: vi
.fn()
.mockRejectedValue(new Error('User cancelled operation')),
displayName: 'Read Many Files',
build: vi.fn(() => ({
execute: mockToolInstance.buildAndExecute,
getDescription: vi.fn(() => 'Mocked tool description'),
})),
};
const viSpy = vi.spyOn(core, 'ReadManyFilesTool');
viSpy.mockImplementation(
() => mockToolInstance as unknown as core.ReadManyFilesTool,
);
const result = await handleAtCommand({
query,
config: mockConfig,
addItem: mockAddItem,
onDebugMessage: mockOnDebugMessage,
messageId: 134,
signal: abortController.signal,
});
expect(result).toEqual({
processedQuery: null,
error: `Exiting due to an error processing the @ command: Error reading files (file.txt): User cancelled operation`,
});
expect(mockAddItem).toHaveBeenCalledWith(
expect.objectContaining({
type: 'tool_group',
tools: [expect.objectContaining({ status: ToolCallStatus.Error })],
}),
134,
);
});
});