From 200139738703db449fae9fcd7c018c06c1827edb Mon Sep 17 00:00:00 2001 From: Shireen Missi <94372015+ShireenMissi@users.noreply.github.com> Date: Fri, 5 Sep 2025 08:55:48 +0100 Subject: [PATCH] fix: Improve error handling for community package installation (#19103) --- .../__tests__/npm-utils.test.ts | 67 +++++++++++++++++++ .../modules/community-packages/npm-utils.ts | 15 +++++ 2 files changed, 82 insertions(+) diff --git a/packages/cli/src/modules/community-packages/__tests__/npm-utils.test.ts b/packages/cli/src/modules/community-packages/__tests__/npm-utils.test.ts index deab10999d..268005fde0 100644 --- a/packages/cli/src/modules/community-packages/__tests__/npm-utils.test.ts +++ b/packages/cli/src/modules/community-packages/__tests__/npm-utils.test.ts @@ -64,6 +64,39 @@ describe('verifyIntegrity', () => { expect(error.cause.message).toContain('Network failure'); } }); + + it('should return generic message for DNS getaddrinfo errors', async () => { + const integrity = 'sha512-somerandomhash=='; + + nock(registryUrl) + .get(`/${encodeURIComponent(packageName)}/${version}`) + .replyWithError('getaddrinfo ENOTFOUND internal.registry.local'); + + try { + await verifyIntegrity(packageName, version, registryUrl, integrity); + throw new Error('Expected error was not thrown'); + } catch (error: any) { + expect(error).toBeInstanceOf(UnexpectedError); + expect(error.message).toBe( + 'Checksum verification failed. Please check your network connection and try again.', + ); + expect(error.cause).toBeUndefined(); + } + }); + + it('should return generic message for DNS ENOTFOUND errors', async () => { + const integrity = 'sha512-somerandomhash=='; + + nock(registryUrl) + .get(`/${encodeURIComponent(packageName)}/${version}`) + .replyWithError('ENOTFOUND some.internal.registry'); + + await expect(verifyIntegrity(packageName, version, registryUrl, integrity)).rejects.toThrow( + new UnexpectedError( + 'Checksum verification failed. Please check your network connection and try again.', + ), + ); + }); }); describe('isVersionExists', () => { @@ -137,4 +170,38 @@ describe('isVersionExists', () => { UnexpectedError, ); }); + + it('should return generic message for DNS getaddrinfo errors', async () => { + nock(registryUrl) + .get(`/${encodeURIComponent(packageName)}/${version}`) + .replyWithError('getaddrinfo ENOTFOUND internal.registry.local'); + + try { + await isVersionExists(packageName, version, registryUrl); + throw new Error('Expected error was not thrown'); + } catch (error: any) { + expect(error).toBeInstanceOf(UnexpectedError); + expect(error.message).toBe( + 'The community nodes service is temporarily unreachable. Please try again later.', + ); + expect(error.cause).toBeUndefined(); + } + }); + + it('should return generic message for DNS ENOTFOUND errors', async () => { + nock(registryUrl) + .get(`/${encodeURIComponent(packageName)}/${version}`) + .replyWithError('ENOTFOUND some.internal.registry'); + + try { + await isVersionExists(packageName, version, registryUrl); + throw new Error('Expected error was not thrown'); + } catch (error: any) { + expect(error).toBeInstanceOf(UnexpectedError); + expect(error.message).toBe( + 'The community nodes service is temporarily unreachable. Please try again later.', + ); + expect(error.cause).toBeUndefined(); + } + }); }); diff --git a/packages/cli/src/modules/community-packages/npm-utils.ts b/packages/cli/src/modules/community-packages/npm-utils.ts index 8038ccc3ac..a818e01a8d 100644 --- a/packages/cli/src/modules/community-packages/npm-utils.ts +++ b/packages/cli/src/modules/community-packages/npm-utils.ts @@ -1,6 +1,11 @@ import axios from 'axios'; import { UnexpectedError } from 'n8n-workflow'; +function isDnsError(error: unknown): boolean { + const message = error instanceof Error ? error.message : String(error); + return message.includes('getaddrinfo') || message.includes('ENOTFOUND'); +} + const REQUEST_TIMEOUT = 30000; export async function verifyIntegrity( @@ -22,6 +27,11 @@ export async function verifyIntegrity( throw new UnexpectedError('Checksum verification failed. Package integrity does not match.'); } } catch (error) { + if (isDnsError(error)) { + throw new UnexpectedError( + 'Checksum verification failed. Please check your network connection and try again.', + ); + } throw new UnexpectedError('Checksum verification failed', { cause: error }); } } @@ -43,6 +53,11 @@ export async function isVersionExists( cause: error, }); } + if (isDnsError(error)) { + throw new UnexpectedError( + 'The community nodes service is temporarily unreachable. Please try again later.', + ); + } throw new UnexpectedError('Failed to check package version existence', { cause: error }); } }