From f6fc97409a88b66c89e3d78a8faa87de631b01fd Mon Sep 17 00:00:00 2001 From: Kevin Heis Date: Mon, 8 Dec 2025 09:42:20 -0800 Subject: [PATCH] Update message on package not found error (#58740) --- package.json | 2 +- src/observability/lib/handle-exceptions.ts | 15 ++---- .../lib/handle-package-not-found.ts | 47 +++++++++++++++++++ 3 files changed, 53 insertions(+), 11 deletions(-) create mode 100644 src/observability/lib/handle-package-not-found.ts diff --git a/package.json b/package.json index 2240ee1767..0dee87296e 100644 --- a/package.json +++ b/package.json @@ -112,7 +112,7 @@ }, "nodemonConfig": { "ext": "ts,json,yml,md,html,scss", - "exec": "tsx", + "exec": "tsx --import ./src/observability/lib/handle-package-not-found.ts", "ignore": [ "assets", "script", diff --git a/src/observability/lib/handle-exceptions.ts b/src/observability/lib/handle-exceptions.ts index bb6fdbd081..1cdd5e46dd 100644 --- a/src/observability/lib/handle-exceptions.ts +++ b/src/observability/lib/handle-exceptions.ts @@ -1,16 +1,11 @@ import FailBot from './failbot' -process.on('uncaughtException', async (err: Error) => { - if ((err as NodeJS.ErrnoException).code === 'MODULE_NOT_FOUND') { - console.error('\n\n🔥 Uh oh! It looks you are missing a required npm module.') - console.error( - 'Please run `npm install` to make sure you have all the required dependencies.\n\n', - ) - } - +process.on('uncaughtException', async (err: Error | unknown) => { console.error(err) try { - FailBot.report(err) + // Type guard to ensure we have an Error object for FailBot + const error = err instanceof Error ? err : new Error(JSON.stringify(err)) + FailBot.report(error) } catch (failBotError) { console.warn('Even sending the uncaughtException error to FailBot failed!') console.error(failBotError) @@ -21,7 +16,7 @@ process.on('unhandledRejection', async (err: Error | unknown) => { console.error(err) try { // Type guard to ensure we have an Error object for FailBot - const error = err instanceof Error ? err : new Error(String(err)) + const error = err instanceof Error ? err : new Error(JSON.stringify(err)) FailBot.report(error) } catch (failBotError) { console.warn('Even sending the unhandledRejection error to FailBot failed!') diff --git a/src/observability/lib/handle-package-not-found.ts b/src/observability/lib/handle-package-not-found.ts new file mode 100644 index 0000000000..2476b21f2d --- /dev/null +++ b/src/observability/lib/handle-package-not-found.ts @@ -0,0 +1,47 @@ +/* +This file adds a custom error message if a package is missing +to prompt the contributor to run `npm ci`. +This handler must be separate from handle-exceptions.ts in order to function. +It's imported in package.json in nodemonConfig, +whereas that is imported in start-server.ts. +This file should not import any packages. +We are suggesting `npm ci` to contributors +to avoid unexpected changes to the package-lock.json file. +All other errors should fall through to the error handler in handle-exceptions.ts. +*/ + +type ErrorWithCode = { + code: string + message: string +} + +const ERROR_TYPES = [ + 'MODULE_NOT_FOUND', + 'ERR_MODULE_NOT_FOUND', + 'ERR_PACKAGE_PATH_NOT_EXPORTED', + 'ERR_PACKAGE_IMPORT_NOT_DEFINED', +] + +function isErrorWithCode(err: unknown): err is ErrorWithCode { + return ( + typeof err === 'object' && + err !== null && + 'code' in err && + 'message' in err && + typeof err.code === 'string' && + typeof err.message === 'string' + ) +} + +process.on('uncaughtException', async (err: Error | unknown) => { + const isMissingModuleError = isErrorWithCode(err) && ERROR_TYPES.includes(err.code) + + if (isMissingModuleError) { + console.error(`${err.code}:`, err.message) + console.error('\n==========================================') + console.error('Some dependencies are missing. Please run:') + console.error(' npm ci') + console.error('==========================================\n') + process.exit(1) + } +})