Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unhandled AbortError causes Unhandled Rejection #303

Closed
zhzLuke96 opened this issue Dec 4, 2024 · 6 comments · Fixed by #357
Closed

Unhandled AbortError causes Unhandled Rejection #303

zhzLuke96 opened this issue Dec 4, 2024 · 6 comments · Fixed by #357
Assignees
Labels
component:js sdk Issue/PR related to JavaScript SDK p1 status:triaged Issue/PR triaged to the corresponding sub-team type:bug Something isn't working

Comments

@zhzLuke96
Copy link

zhzLuke96 commented Dec 4, 2024

Description of the bug:

When an AbortController is triggered (e.g., via timeout or manual abort), the SDK throws an AbortError that cannot be caught using try...catch. This results in an Unhandled Rejection, forcing developers to handle it globally with process.on('unhandledRejection', ...).

Here is the error log:

[Unhandled rejection]
DOMException [AbortError]: This operation was aborted
    at new DOMException (node:internal/per_context/domexception:53:5)
    at AbortController.abort (node:internal/abort_controller:401:18)
    at EventTarget.<anonymous> (O:\work_space\github.com\@lenML\project1\node_modules\.pnpm\@google+generative-ai@0.21.0\node_modules\@google\generative-ai\dist\index.js:430:28)         
    at [nodejs.internal.kHybridDispatch] (node:internal/event_target:786:20)
    at EventTarget.dispatchEvent (node:internal/event_target:721:26)
    at abortSignal (node:internal/abort_controller:369:10)
    at AbortController.abort (node:internal/abort_controller:403:5)

Suspected problematic code:

/**
* Reads a raw stream from the fetch response and join incomplete
* chunks, returning a new stream that provides a single complete
* GenerateContentResponse in each iteration.
*/
export function getResponseStream<T>(
inputStream: ReadableStream<string>,
): ReadableStream<T> {
const reader = inputStream.getReader();
const stream = new ReadableStream<T>({
start(controller) {
let currentText = "";
return pump();
function pump(): Promise<(() => Promise<void>) | undefined> {
return reader.read().then(({ value, done }) => {
if (done) {
if (currentText.trim()) {
controller.error(
new GoogleGenerativeAIError("Failed to parse stream"),
);
return;
}
controller.close();
return;
}
currentText += value;
let match = currentText.match(responseLineRE);
let parsedResponse: T;
while (match) {
try {
parsedResponse = JSON.parse(match[1]);
} catch (e) {
controller.error(
new GoogleGenerativeAIError(
`Error parsing JSON response: "${match[1]}"`,
),
);
return;
}
controller.enqueue(parsedResponse);
currentText = currentText.substring(match[0].length);
match = currentText.match(responseLineRE);
}
return pump();
});
}
},
});
return stream;
}

Actual vs expected behavior:

  • Actual:
    AbortError is not catchable by try...catch, causing unhandled promise rejections.

  • Expected:
    AbortError should be catchable within try...catch in user code, without requiring global error handlers.

Any other information you'd like to share?

none

@gmKeshari gmKeshari added type:bug Something isn't working status:triaged Issue/PR triaged to the corresponding sub-team component:js sdk Issue/PR related to JavaScript SDK labels Dec 9, 2024
@arnab710
Copy link

Facing the same issue.
@zhzLuke96 did you find any solution?

@zhzLuke96
Copy link
Author

@arnab710 In fact, I mentioned the solution above—using process.on('unhandledRejection', ...) to handle it (DOMException).

Specifically, the code would look something like this:

process.on("unhandledRejection", (err) => {
  if (err instanceof DOMException && err.name === "AbortError") {
    console.log(`[Abort] ${err.message}`);
    return;
  }

  console.error(`[Unhandled rejection]`);
  console.error(err);
});

This is one way to address it. However, it’s obvious that you won’t be able to identify where this error originated. You can’t tell whether it’s an error from your system or from generative-ai-js.

Of course, you can also choose to use a package patch and fix the stream-reader.ts code yourself. Simply put:

(I haven't actually used this method; it's just a guess that it might work, though there may be additional conditions to consider.)

stream-reader.ts 91 line

function pump(): Promise<(() => Promise<void>) | undefined> {
  return reader.read().then(({ value, done }) => {
    // ...
  }).catch(err => {
    // ... catch Abort Error here
  });
}

@danny-avila
Copy link

This is affecting me now, handling this at the global process level is not ideal. Would like to see some proper handling here at the SDK-level.

@KiGamji
Copy link

KiGamji commented Feb 21, 2025

I don't really know how this error is occuring, but it often happens with Gemini completions in Russian language. If that internally is not related to the SDK itself, perhaps some of the SDK developers could report that to Google?

@hkt74 hkt74 self-assigned this Feb 25, 2025
@hkt74 hkt74 marked this as a duplicate of #338 Feb 26, 2025
@IvanLH IvanLH added the p1 label Feb 26, 2025
@danny-avila
Copy link

I updated the package for the fix, but catching the error is still necessary at the global level, though I am also able to catch it from the stream now.

@hkt74
Copy link
Collaborator

hkt74 commented Mar 4, 2025

@danny-avila interesting, would you mind sharing the stack trace? maybe we need to catch the abort error beyond the stream reader.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
component:js sdk Issue/PR related to JavaScript SDK p1 status:triaged Issue/PR triaged to the corresponding sub-team type:bug Something isn't working
Projects
None yet
Development

Successfully merging a pull request may close this issue.

8 participants