Skip to content

Conversation

@kamja44
Copy link
Contributor

@kamja44 kamja44 commented Dec 8, 2025

  • Add outer try-catch block to catch initialization errors
  • Connect async IIFE rejection to Promise reject handler
  • Add tests for error scenarios during client initialization
  • Prevents unhandled promise rejection when McpClient constructor throws
  • Ensures one server failure doesn't stop other servers from starting

Fixes #14700

Summary

Fixes a structural issue in McpClientManager.addOrUpdateServer() that causes unhandled promise rejections during MCP server initialization. The Promise constructor received an unused _reject parameter, and the async IIFE lacked proper error handling, causing crashes when errors occurred in the McpClient constructor or disconnect method.

Details

Root cause: The current implementation creates a Promise with _reject parameter but never uses it. The async IIFE has no .catch() handler, so errors thrown before the try block become unhandled rejections.

Changes made:

  1. Changed _reject to reject and properly connected it
  2. Added outer try-catch to catch initialization errors (constructor, disconnect)
  3. Added .catch(reject) to async IIFE to handle any uncaught errors
  4. Added proper error logging via coreEvents.emitFeedback

Files modified:

  • packages/core/src/tools/mcp-client-manager.ts (lines 162-220)
  • packages/core/src/tools/mcp-client-manager.test.ts (added 2 test cases)

Note: This is NOT a duplicate of #8365. That issue was about MCP timeout errors in gemini.mjs and is already closed. This PR fixes a different structural problem in mcp-client-manager.ts that could cause unhandled rejections during initialization.

Related Issues

Fixes #14700

How to Validate

  1. Run unit tests:
npm run test mcp-client-manager.test.ts

## Pre-Merge Checklist

<!-- Check all that apply before requesting review or merging. -->

- [ ] Updated relevant documentation and README (if needed)
- [O] Added/updated tests (if needed)
- [ ] Noted breaking changes (if any)
- [ ] Validated on required platforms/methods:
  - [ ] MacOS
    - [ ] npm run
    - [ ] npx
    - [ ] Docker
    - [ ] Podman
    - [ ] Seatbelt
  - [ ] Windows
    - [ ] npm run
    - [ ] npx
    - [ ] Docker
  - [ ] Linux
    - [O] npm run
    - [ ] npx
    - [ ] Docker

- Add outer try-catch block to catch initialization errors
- Connect async IIFE rejection to Promise reject handler
- Add tests for error scenarios during client initialization
- Prevents unhandled promise rejection when McpClient constructor throws
- Ensures one server failure doesn't stop other servers from starting

Fixes google-gemini#14700
@kamja44 kamja44 requested a review from a team as a code owner December 8, 2025 10:14
@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @kamja44, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request addresses a critical structural flaw in the McpClientManager that led to unhandled promise rejections during MCP server initialization. By enhancing error handling within the Promise constructor and the asynchronous IIFE, the changes ensure that the application remains stable even when McpClient operations, such as construction or disconnection, encounter errors. This prevents crashes and allows other servers to initialize successfully, improving overall system resilience.

Highlights

  • Error Handling Improvement: Implemented an outer try-catch block to gracefully handle initialization errors within the McpClientManager.
  • Promise Rejection Connection: The _reject parameter in the Promise constructor was renamed to reject and properly connected to catch asynchronous errors.
  • Asynchronous IIFE Error Catching: Added a .catch(reject) handler to the async Immediately Invoked Function Expression (IIFE) to prevent unhandled promise rejections.
  • Robust Server Initialization: Ensures that a failure in one server's initialization or disconnection process does not halt the startup of other servers.
  • Enhanced Error Logging: Integrated coreEvents.emitFeedback for proper error logging when MCP server initialization fails.
  • New Test Cases: Added dedicated test cases to validate the new error handling mechanisms during client initialization and within the async IIFE.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@kamja44
Copy link
Contributor Author

kamja44 commented Dec 8, 2025

I also left this explanation in the ISSUE, but I am attaching it here in the PR comments as well to clarify why this is not a duplicate.

Why this is NOT a duplicate of #8365

#8365 (Closed):

Issue: MCP timeout error during user confirmation

Location: gemini.mjs timeout handler

Cause: MCP error -32001: Request timed out

Status: Already fixed and closed

This PR (#14700):

Issue: Structural problem in promise error handling during MCP initialization

Location: packages/core/src/tools/mcp-client-manager.ts (addOrUpdateServer method)

Cause: Unused _reject parameter + missing .catch() in the async IIFE

Status: Preventive fix to avoid potential crashes

Key differences:

Different code files and methods

Different root causes (timeout vs improper promise error handling)

Different scenarios (user confirmation timeout vs client initialization failures)

This PR is a preventive robustness improvement, not a fix for a previously reported crash

This PR addresses a structural issue that may lead to similar symptoms but has an entirely different root cause.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request effectively addresses an unhandled promise rejection within McpClientManager by introducing proper error handling inside the promise executor. The new tests also correctly validate the fix for server initialization failures. However, my review has identified a critical side effect of the change: it introduces a new path for promise rejection that leads to another unhandled promise rejection later in the same function. My comment provides a detailed explanation of this issue and a recommended solution to make the fix complete.

Address review feedback: Change .then() to .finally() on line 238 to ensure
cleanup logic runs regardless of promise resolution, preventing unhandled
rejection when currentDiscoveryPromise rejects.

- Replace .then((_) => ...) with void finally(() => ...)
- Remove eslint-disable comment (no longer needed)
- Ensures discoveryState cleanup happens on both success and failure
@Adib234
Copy link
Contributor

Adib234 commented Dec 30, 2025

hi, sorry that no one has reviewed your PR! I can take a look at this if you are still working on it!

@kamja44
Copy link
Contributor Author

kamja44 commented Dec 31, 2025

hi, sorry that no one has reviewed your PR! I can take a look at this if you are still working on it!

Hi, thanks so much for taking a look — I really appreciate it!
Yes, I’m still working on this and would love your feedback whenever you have time

const currentPromise = this.discoveryPromise;
// eslint-disable-next-line @typescript-eslint/no-floating-promises
currentPromise.then((_) => {
void currentPromise.finally(() => {
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we attach a catch to this promise?

void currentPromise
  .finally(() => {
    if (currentPromise === this.discoveryPromise) {
      this.discoveryPromise = undefined;
      this.discoveryState = MCPDiscoveryState.COMPLETED;
    }
  })
  .catch(() => {}); // Prevents unhandled rejection from the .finally branch

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added the .catch() to handle any errors from the finally block. Thanks!

Copy link
Contributor

Choose a reason for hiding this comment

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

We have another unhandled promise on line 234

this.discoveryPromise = this.discoveryPromise.then(

If the promise gets rejected there's nothing to handle this, so can we add the catch as well?

if (this.discoveryPromise) {
  // Ensure the next discovery starts regardless of the previous one's success/failure
  this.discoveryPromise = this.discoveryPromise
    .catch(() => {}) 
    .then(() => currentDiscoveryPromise);
}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Without the .catch(), the next discovery wouldn't run if the previous one failed. Fixed by adding .catch() before .then(). Thanks for catching this important issue!

kamja44 and others added 2 commits January 3, 2026 00:10
… chain

- Add catch to finally chain to prevent unhandled rejections
- Add catch before then to ensure discovery continues on previous failure
Copy link
Contributor

@Adib234 Adib234 left a comment

Choose a reason for hiding this comment

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

Thank you for this! LGTM 🚀

@Adib234 Adib234 added this pull request to the merge queue Jan 2, 2026
Merged via the queue into google-gemini:main with commit 8a0190c Jan 2, 2026
20 checks passed
thacio added a commit to thacio/auditaria that referenced this pull request Jan 24, 2026
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.

Fix unhandled promise rejection in MCP client manager

3 participants