From fc8bfb669122058ca339072d5bc6b96623e63636 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9F=D0=B5=D1=80=D0=B2=D0=BE=D0=B2=20=D0=90=D1=80=D1=82?= =?UTF-8?q?=D0=B5=D0=BC?= Date: Tue, 7 Jul 2026 01:27:07 +0300 Subject: [PATCH] =?UTF-8?q?=D0=91=D0=A3=D0=A0-68=20=D0=94=D0=BE=D0=B1?= =?UTF-8?q?=D0=B0=D0=B2=D0=B8=D0=BB=20=D0=BE=D0=B1=D1=80=D0=B0=D0=B1=D0=BE?= =?UTF-8?q?=D1=82=D0=BA=D1=83=20ECONNREFUSED=20=D0=B8=20=D1=81=D0=BE=D1=85?= =?UTF-8?q?=D1=80=D0=B0=D0=BD=D0=B5=D0=BD=D0=B8=D0=B5=20=D1=81=D0=B5=D1=80?= =?UTF-8?q?=D0=B2=D0=B8=D1=81=D0=B0=20=D0=B6=D0=B8=D0=B2=D1=8B=D0=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/index.ts | 1 + app/src/process-errors.ts | 57 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+) create mode 100644 app/src/process-errors.ts diff --git a/app/src/index.ts b/app/src/index.ts index 9fbd394..1cd4f4e 100644 --- a/app/src/index.ts +++ b/app/src/index.ts @@ -1,3 +1,4 @@ +import './process-errors.js' import './handlers/index.js' import './http/server.js' import './mqtt.js' diff --git a/app/src/process-errors.ts b/app/src/process-errors.ts new file mode 100644 index 0000000..4b33394 --- /dev/null +++ b/app/src/process-errors.ts @@ -0,0 +1,57 @@ +type ErrorWithCause = Error & { + cause?: unknown +} + +type NodeError = Error & { + code?: string + address?: string + port?: number +} + +// Undici кладёт системную ошибку соединения внутрь error.cause. +function getFetchCause(error: unknown): NodeError | null { + if (!(error instanceof Error)) { + return null + } + + const cause = (error as ErrorWithCause).cause + return cause instanceof Error ? (cause as NodeError) : null +} + +// Обрабатываем только подсвеченный кейс: backend отказал в TCP-соединении. +function isFetchConnectionRefused(error: unknown): boolean { + if (!(error instanceof Error)) { + return false + } + + const cause = getFetchCause(error) + return error.message === 'fetch failed' && cause?.code === 'ECONNREFUSED' +} + +// Логируем короткую диагностическую форму без огромного stack trace. +function formatFetchError(error: unknown): object { + if (!(error instanceof Error)) { + return { message: String(error) } + } + + const cause = getFetchCause(error) + return { + message: error.message, + cause: cause?.message, + code: cause?.code, + address: cause?.address, + port: cause?.port, + } +} + +// Последний рубеж защиты для fire-and-forget fetch-запросов. +// Если backend недоступен, Node.js 22 может превратить незахваченный +// fetch failed / ECONNREFUSED в падение всего процесса mqtt-worker. +process.on('unhandledRejection', (error) => { + if (isFetchConnectionRefused(error)) { + console.error('process.unhandled_fetch_refused', formatFetchError(error)) + return + } + + console.error('process.unhandled_rejection', error) +}) -- 2.49.1