fix(cli): prevent exit on non-fatal tool errors (#10671)

This commit is contained in:
Jerop Kipruto
2025-10-09 17:20:20 -04:00
committed by GitHub
parent a8379d1f4b
commit 5f96eba54a
4 changed files with 160 additions and 93 deletions
+76 -76
View File
@@ -291,90 +291,90 @@ describe('errors', () => {
).mockReturnValue(OutputFormat.JSON);
});
it('should format error as JSON and exit with default code', () => {
expect(() => {
handleToolError(toolName, toolError, mockConfig);
}).toThrow('process.exit called with code: 54');
expect(consoleErrorSpy).toHaveBeenCalledWith(
JSON.stringify(
{
error: {
type: 'FatalToolExecutionError',
message: 'Error executing tool test-tool: Tool failed',
code: 54,
},
},
null,
2,
),
);
});
it('should use custom error code', () => {
expect(() => {
handleToolError(toolName, toolError, mockConfig, 'CUSTOM_TOOL_ERROR');
}).toThrow('process.exit called with code: 54');
expect(consoleErrorSpy).toHaveBeenCalledWith(
JSON.stringify(
{
error: {
type: 'FatalToolExecutionError',
message: 'Error executing tool test-tool: Tool failed',
code: 'CUSTOM_TOOL_ERROR',
},
},
null,
2,
),
);
});
it('should use numeric error code and exit with that code', () => {
expect(() => {
handleToolError(toolName, toolError, mockConfig, 500);
}).toThrow('process.exit called with code: 500');
expect(consoleErrorSpy).toHaveBeenCalledWith(
JSON.stringify(
{
error: {
type: 'FatalToolExecutionError',
message: 'Error executing tool test-tool: Tool failed',
code: 500,
},
},
null,
2,
),
);
});
it('should prefer resultDisplay over error message', () => {
expect(() => {
describe('non-fatal errors', () => {
it('should log error message to stderr without exiting for recoverable errors', () => {
handleToolError(
toolName,
toolError,
mockConfig,
'DISPLAY_ERROR',
'invalid_tool_params',
);
expect(consoleErrorSpy).toHaveBeenCalledWith(
'Error executing tool test-tool: Tool failed',
);
// Should not exit for non-fatal errors
expect(processExitSpy).not.toHaveBeenCalled();
});
it('should not exit for file not found errors', () => {
handleToolError(toolName, toolError, mockConfig, 'file_not_found');
expect(consoleErrorSpy).toHaveBeenCalledWith(
'Error executing tool test-tool: Tool failed',
);
expect(processExitSpy).not.toHaveBeenCalled();
});
it('should not exit for permission denied errors', () => {
handleToolError(toolName, toolError, mockConfig, 'permission_denied');
expect(consoleErrorSpy).toHaveBeenCalledWith(
'Error executing tool test-tool: Tool failed',
);
expect(processExitSpy).not.toHaveBeenCalled();
});
it('should not exit for path not in workspace errors', () => {
handleToolError(
toolName,
toolError,
mockConfig,
'path_not_in_workspace',
);
expect(consoleErrorSpy).toHaveBeenCalledWith(
'Error executing tool test-tool: Tool failed',
);
expect(processExitSpy).not.toHaveBeenCalled();
});
it('should prefer resultDisplay over error message', () => {
handleToolError(
toolName,
toolError,
mockConfig,
'invalid_tool_params',
'Display message',
);
}).toThrow('process.exit called with code: 54');
expect(consoleErrorSpy).toHaveBeenCalledWith(
JSON.stringify(
{
error: {
type: 'FatalToolExecutionError',
message: 'Error executing tool test-tool: Display message',
code: 'DISPLAY_ERROR',
expect(consoleErrorSpy).toHaveBeenCalledWith(
'Error executing tool test-tool: Display message',
);
expect(processExitSpy).not.toHaveBeenCalled();
});
});
describe('fatal errors', () => {
it('should exit immediately for NO_SPACE_LEFT errors', () => {
expect(() => {
handleToolError(toolName, toolError, mockConfig, 'no_space_left');
}).toThrow('process.exit called with code: 54');
expect(consoleErrorSpy).toHaveBeenCalledWith(
JSON.stringify(
{
error: {
type: 'FatalToolExecutionError',
message: 'Error executing tool test-tool: Tool failed',
code: 'no_space_left',
},
},
},
null,
2,
),
);
null,
2,
),
);
});
});
});
});