From 641d29b8f55aafd222a34fc07d3c1d4f711319fb Mon Sep 17 00:00:00 2001 From: ledgerpilot Date: Tue, 7 Apr 2026 16:49:55 -0700 Subject: [PATCH 1/3] =?UTF-8?q?fix:=20address=20issue=20#2027=20=E2=80=94?= =?UTF-8?q?=20[BUG]=20CLI=20hangs=20indefinitely=20when=20`--registry-url?= =?UTF-8?q?=20`points=20to?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- cli/src/utils/generate/registry.ts | 40 ++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 cli/src/utils/generate/registry.ts diff --git a/cli/src/utils/generate/registry.ts b/cli/src/utils/generate/registry.ts new file mode 100644 index 00000000..57982885 --- /dev/null +++ b/cli/src/utils/generate/registry.ts @@ -0,0 +1,40 @@ +import fetch, { AbortController } from 'node-fetch'; // Assuming `node-fetch` is used. If `fetch` is globally available (Node.js 18+), AbortController is also global, and this line can be adjusted to `const AbortController = globalThis.AbortController;` or simply rely on global scope. + +const REGISTRY_TIMEOUT_MS = 5000; // 5 seconds timeout for registry validation + +export async function registryValidation(registryUrl: string): Promise { + const controller = new AbortController(); + const timeoutId = setTimeout(() => controller.abort(), REGISTRY_TIMEOUT_MS); + + try { + const response = await fetch(registryUrl, { + method: 'HEAD', // Use HEAD for a lightweight check, as we only care about reachability + signal: controller.signal, + }); + + if (!response.ok) { + // If the response is not OK (e.g., 404, 500), it implies the URL is reachable but not a valid registry endpoint or has server-side issues. + throw new Error( + `Registry URL '${registryUrl}' returned an unsuccessful status ${response.status} (${response.statusText}). ` + + `It might not be a valid registry endpoint or is experiencing server issues.`, + ); + } + } catch (error: any) { + if (error.name === 'AbortError') { + // Handle timeout specifically + throw new Error( + `Failed to connect to registry URL '${registryUrl}' within ${REGISTRY_TIMEOUT_MS / 1000} seconds. ` + + `This typically indicates a network issue, an incorrect URL, or that the registry host is unreachable/slow.`, + ); + } else { + // Handle other network-related errors (DNS lookup failed, connection refused, etc.) + throw new Error( + `Failed to connect to registry URL '${registryUrl}'. ` + + `Please ensure the host is reachable and the URL is correct. ` + + `Original network error: ${error.message}`, + ); + } + } finally { + clearTimeout(timeoutId); // Ensure the timeout is cleared to prevent resource leaks + } +} \ No newline at end of file From 2ee25d077a3e0ff3bd9b18a045f434286c5211d6 Mon Sep 17 00:00:00 2001 From: ledgerpilot Date: Tue, 7 Apr 2026 17:25:28 -0700 Subject: [PATCH 2/3] =?UTF-8?q?fix:=20address=20issue=20#2027=20=E2=80=94?= =?UTF-8?q?=20[BUG]=20CLI=20hangs=20indefinitely=20when=20`--registry-url?= =?UTF-8?q?=20`points=20to?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- cli/src/utils/generate/registry.ts | 36 ++++++++++-------------------- 1 file changed, 12 insertions(+), 24 deletions(-) diff --git a/cli/src/utils/generate/registry.ts b/cli/src/utils/generate/registry.ts index 57982885..178b60f0 100644 --- a/cli/src/utils/generate/registry.ts +++ b/cli/src/utils/generate/registry.ts @@ -1,5 +1,3 @@ -import fetch, { AbortController } from 'node-fetch'; // Assuming `node-fetch` is used. If `fetch` is globally available (Node.js 18+), AbortController is also global, and this line can be adjusted to `const AbortController = globalThis.AbortController;` or simply rely on global scope. - const REGISTRY_TIMEOUT_MS = 5000; // 5 seconds timeout for registry validation export async function registryValidation(registryUrl: string): Promise { @@ -7,34 +5,24 @@ export async function registryValidation(registryUrl: string): Promise { const timeoutId = setTimeout(() => controller.abort(), REGISTRY_TIMEOUT_MS); try { - const response = await fetch(registryUrl, { - method: 'HEAD', // Use HEAD for a lightweight check, as we only care about reachability + await fetch(registryUrl, { + method: 'HEAD', // Use HEAD for a lightweight reachability check signal: controller.signal, }); - - if (!response.ok) { - // If the response is not OK (e.g., 404, 500), it implies the URL is reachable but not a valid registry endpoint or has server-side issues. - throw new Error( - `Registry URL '${registryUrl}' returned an unsuccessful status ${response.status} (${response.statusText}). ` + - `It might not be a valid registry endpoint or is experiencing server issues.`, - ); - } - } catch (error: any) { + } catch (error: any) { // Use 'any' for error type to handle both AbortError and other network errors if (error.name === 'AbortError') { - // Handle timeout specifically - throw new Error( - `Failed to connect to registry URL '${registryUrl}' within ${REGISTRY_TIMEOUT_MS / 1000} seconds. ` + - `This typically indicates a network issue, an incorrect URL, or that the registry host is unreachable/slow.`, - ); - } else { - // Handle other network-related errors (DNS lookup failed, connection refused, etc.) throw new Error( - `Failed to connect to registry URL '${registryUrl}'. ` + - `Please ensure the host is reachable and the URL is correct. ` + - `Original network error: ${error.message}`, + `Connection to registry URL '${registryUrl}' timed out after ${REGISTRY_TIMEOUT_MS / 1000} seconds. ` + + `Please ensure the URL is correct and the host is reachable.` ); } + // Handle other network-related errors (e.g., DNS resolution failure, connection refused) + throw new Error( + `Failed to connect to registry URL '${registryUrl}'. ` + + `Network error: ${error.message}. ` + + `Please check your network connection and the provided registry URL.` + ); } finally { - clearTimeout(timeoutId); // Ensure the timeout is cleared to prevent resource leaks + clearTimeout(timeoutId); } } \ No newline at end of file From be7bcb6955e5de61a70ca10c0d6fb599b5d836b0 Mon Sep 17 00:00:00 2001 From: ledgerpilot Date: Tue, 7 Apr 2026 18:56:45 -0700 Subject: [PATCH 3/3] chore: add changeset for add-timeout-to-registry-url-check --- .changeset/add-timeout-to-registry-url-check.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/add-timeout-to-registry-url-check.md diff --git a/.changeset/add-timeout-to-registry-url-check.md b/.changeset/add-timeout-to-registry-url-check.md new file mode 100644 index 00000000..312565f8 --- /dev/null +++ b/.changeset/add-timeout-to-registry-url-check.md @@ -0,0 +1,5 @@ +--- +"@asyncapi/cli": patch +--- + +fix: add timeout and AbortController to registry URL validation to prevent CLI hang on unreachable endpoints