From 8850a35f9ea69ad065fc11f567bcdfd0e7ccbb93 Mon Sep 17 00:00:00 2001 From: Sanchit2662 Date: Wed, 14 Jan 2026 23:31:09 +0530 Subject: [PATCH 1/2] fix(backend): clean up process event listeners on backend stop Signed-off-by: Sanchit2662 --- .../src/wiring/BackendInitializer.ts | 22 +++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/packages/backend-app-api/src/wiring/BackendInitializer.ts b/packages/backend-app-api/src/wiring/BackendInitializer.ts index c86fb0b29c..2d32306385 100644 --- a/packages/backend-app-api/src/wiring/BackendInitializer.ts +++ b/packages/backend-app-api/src/wiring/BackendInitializer.ts @@ -157,6 +157,8 @@ export class BackendInitializer { #serviceRegistry: ServiceRegistry; #registeredFeatures = new Array>(); #registeredFeatureLoaders = new Array(); + #unhandledRejectionHandler?: (reason: Error) => void; + #uncaughtExceptionHandler?: (error: Error) => void; constructor(defaultApiFactories: ServiceFactory[]) { this.#serviceRegistry = ServiceRegistry.create([...defaultApiFactories]); @@ -265,16 +267,18 @@ export class BackendInitializer { coreServices.rootLogger, 'root', ); - process.on('unhandledRejection', (reason: Error) => { + this.#unhandledRejectionHandler = (reason: Error) => { rootLogger ?.child({ type: 'unhandledRejection' }) ?.error('Unhandled rejection', reason); - }); - process.on('uncaughtException', error => { + }; + this.#uncaughtExceptionHandler = (error: Error) => { rootLogger ?.child({ type: 'uncaughtException' }) ?.error('Uncaught exception', error); - }); + }; + process.on('unhandledRejection', this.#unhandledRejectionHandler); + process.on('uncaughtException', this.#uncaughtExceptionHandler); } // Initialize all root scoped services @@ -490,6 +494,16 @@ export class BackendInitializer { // Once all plugin shutdown hooks are done, run root shutdown hooks. await rootLifecycleService.shutdown(); + + // Clean up process event listeners to prevent memory leaks and duplicate logging + if (this.#unhandledRejectionHandler) { + process.off('unhandledRejection', this.#unhandledRejectionHandler); + this.#unhandledRejectionHandler = undefined; + } + if (this.#uncaughtExceptionHandler) { + process.off('uncaughtException', this.#uncaughtExceptionHandler); + this.#uncaughtExceptionHandler = undefined; + } } // Bit of a hacky way to grab the lifecycle services, potentially find a nicer way to do this From 04db26b94f53d06e5299c26e38b5ccb22f2a93b0 Mon Sep 17 00:00:00 2001 From: Sanchit2662 Date: Thu, 15 Jan 2026 22:10:25 +0530 Subject: [PATCH 2/2] chore: add changeset for backend listener cleanup Signed-off-by: Sanchit2662 --- .changeset/dull-hounds-return.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/dull-hounds-return.md diff --git a/.changeset/dull-hounds-return.md b/.changeset/dull-hounds-return.md new file mode 100644 index 0000000000..9123920984 --- /dev/null +++ b/.changeset/dull-hounds-return.md @@ -0,0 +1,5 @@ +--- +'@backstage/backend-app-api': patch +--- + +Clean up process event listeners on backend stop to prevent leaks