Skip to content

Commit cb8c08a

Browse files
fix(generate): preserve underlying error when validating registry URL
1 parent 16c5d9c commit cb8c08a

File tree

3 files changed

+81
-2
lines changed

3 files changed

+81
-2
lines changed

PR_DESCRIPTION.md

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
Title: fix: preserve underlying error when validating registry URL
2+
3+
Summary
4+
5+
The `registryValidation` function in `src/utils/generate/registry.ts` previously caught all errors during registry URL validation and rethrew a generic Error that discarded the original error details. This made troubleshooting impossible for users because the underlying cause (DNS resolution, network timeout, TLS/SSL, proxy issues, etc.) was lost.
6+
7+
What I changed
8+
9+
- Capture the original error in the catch block and include its message in the thrown error text (`Caused by: ...`).
10+
- Attach the original error to the thrown Error's `cause` property when possible so callers can inspect it programmatically.
11+
12+
Why
13+
14+
Preserving the original error helps users and maintainers diagnose registry connection issues more effectively.
15+
16+
Testing
17+
18+
- Ran `npm ci` and executed the CLI test suite (`npm run cli:test`). The repository has many test suites; I ran the CLI tests locally to validate behavior. The change is small and focused; adding a dedicated unit test for `registryValidation` is straightforward and I can add it if you'd like.
19+
20+
Notes
21+
22+
- I avoided changing the public API; callers still receive an Error but with helpful details and `error.cause` populated when available.
23+
24+
Suggested PR body for GitHub
25+
26+
This PR fixes #2013 — preserve the underlying error when validating `--registry-url` so users can see the root cause of failures (DNS, network, SSL, proxy, etc.).
27+
28+
Would you like me to create the GitHub PR automatically using the GitHub CLI (`gh`) or open a draft PR URL for you?

src/utils/generate/registry.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,15 @@ export async function registryValidation(registryUrl?: string, registryAuth?: st
1313
if (response.status === 401 && !registryAuth && !registryToken) {
1414
throw new Error('You Need to pass either registryAuth in username:password encoded in Base64 or need to pass registryToken');
1515
}
16-
} catch {
17-
throw new Error(`Can't fetch registryURL: ${registryUrl}`);
16+
} catch (err) {
17+
const causeMsg = err instanceof Error ? err.message : String(err);
18+
const errToThrow = new Error(`Can't fetch registryURL: ${registryUrl}\nCaused by: ${causeMsg}`);
19+
try {
20+
// prefer using the standardized `cause` when available
21+
(errToThrow as any).cause = err;
22+
} catch (_) {
23+
// ignore if we can't attach cause
24+
}
25+
throw errToThrow;
1826
}
1927
}

test/unit/registry.test.ts

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import { expect } from 'chai';
2+
import { registryValidation } from '../../src/utils/generate/registry';
3+
4+
describe('registryValidation', () => {
5+
const originalFetch = (globalThis as any).fetch;
6+
7+
afterEach(() => {
8+
(globalThis as any).fetch = originalFetch;
9+
});
10+
11+
it('returns undefined when no url provided', async () => {
12+
const result = await registryValidation(undefined);
13+
expect(result).to.equal(undefined);
14+
}).timeout(5000);
15+
16+
it('wraps fetch errors and preserves cause', async () => {
17+
const networkError = new Error('getaddrinfo ENOTFOUND my-registry.example.com');
18+
(globalThis as any).fetch = () => { throw networkError; };
19+
20+
try {
21+
await registryValidation('https://my-registry.example.com');
22+
throw new Error('Expected registryValidation to throw');
23+
} catch (err: any) {
24+
expect(String(err.message)).to.contain("Can't fetch registryURL: https://my-registry.example.com");
25+
expect(String(err.message)).to.contain('Caused by: getaddrinfo ENOTFOUND my-registry.example.com');
26+
expect((err as any).cause).to.equal(networkError);
27+
}
28+
}).timeout(5000);
29+
30+
it('wraps 401 auth response and preserves cause message', async () => {
31+
(globalThis as any).fetch = async () => ({ status: 401 });
32+
33+
try {
34+
await registryValidation('https://my-registry.example.com');
35+
throw new Error('Expected registryValidation to throw');
36+
} catch (err: any) {
37+
expect(String(err.message)).to.contain("Can't fetch registryURL: https://my-registry.example.com");
38+
expect(String(err.message)).to.contain('Caused by: You Need to pass either registryAuth');
39+
expect((err as any).cause).to.be.instanceOf(Error);
40+
expect((err as any).cause.message).to.contain('You Need to pass either registryAuth');
41+
}
42+
}).timeout(5000);
43+
});

0 commit comments

Comments
 (0)