fix: unhandled rejection from stale runtime (#36670)

This commit is contained in:
Douglas Gubert 2025-08-18 16:27:45 -03:00 committed by GitHub
parent c48070da8d
commit 42979690f3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 47 additions and 21 deletions

View File

@ -0,0 +1,6 @@
---
'@rocket.chat/meteor': patch
'@rocket.chat/apps-engine': patch
---
Fixes an issue that would cause the chat server to crash with an unhandled rejection in some cases

View File

@ -85,6 +85,8 @@ export type DenoRuntimeOptions = {
timeout: number;
};
type AbortFunction = (reason?: any) => void;
export class DenoRuntimeSubprocessController extends EventEmitter {
private deno: child_process.ChildProcess | undefined;
@ -322,13 +324,17 @@ export class DenoRuntimeSubprocessController extends EventEmitter {
const request = jsonrpc.request(id, message.method, message.params);
const promise = this.waitForResponse(request, options).finally(() => {
const { promise, abort } = this.waitForResponse(request, options);
try {
this.messenger.send(request);
} catch (e) {
abort(e);
}
return promise.finally(() => {
this.debug('Request %s for method %s took %dms', id, message.method, Date.now() - start);
});
this.messenger.send(request);
return promise;
}
private waitUntilReady(): Promise<void> {
@ -353,27 +359,41 @@ export class DenoRuntimeSubprocessController extends EventEmitter {
});
}
private waitForResponse(req: jsonrpc.RequestObject, options = this.options): Promise<unknown> {
return new Promise((resolve, reject) => {
const responseCallback = (result: unknown, error: jsonrpc.IParsedObjectError['payload']['error']) => {
clearTimeout(timeoutId);
private waitForResponse(req: jsonrpc.RequestObject, options = this.options): { abort: AbortFunction; promise: Promise<unknown> } {
const controller = new AbortController();
const { abort, signal } = controller;
if (error) {
reject(error);
}
return {
abort: abort.bind(controller),
promise: new Promise((resolve, reject) => {
const eventName = `result:${req.id}`;
resolve(result);
};
const responseCallback = (result: unknown, error: jsonrpc.IParsedObjectError['payload']['error'] | Error) => {
this.off(eventName, responseCallback);
clearTimeout(timeoutId);
const eventName = `result:${req.id}`;
if (error) {
reject(error);
}
const timeoutId = setTimeout(() => {
this.off(eventName, responseCallback);
reject(new Error(`[${this.getAppId()}] Request "${req.id}" for method "${req.method}" timed out`));
}, options.timeout);
resolve(result);
};
this.once(eventName, responseCallback);
});
const timeoutId = setTimeout(
() =>
responseCallback(
undefined,
new Error(`[${this.getAppId()}] Request "${req.id}" for method "${req.method}" timed out after ${options.timeout}ms`),
),
options.timeout,
);
signal.onabort = () =>
responseCallback(undefined, signal.reason instanceof Error ? signal.reason : new Error(String(signal.reason)));
this.once(eventName, responseCallback);
}),
};
}
private onReady(): void {