diff --git a/.changeset/unlucky-numbers-cheer.md b/.changeset/unlucky-numbers-cheer.md new file mode 100644 index 0000000000..8c320d6686 --- /dev/null +++ b/.changeset/unlucky-numbers-cheer.md @@ -0,0 +1,5 @@ +--- +"viem": patch +--- + +Added `cause` in `HttpRequestError`. diff --git a/src/errors/base.ts b/src/errors/base.ts index 5170893220..2c6c815812 100644 --- a/src/errors/base.ts +++ b/src/errors/base.ts @@ -1,20 +1,13 @@ import { getVersion } from './utils.js' type BaseErrorParameters = { + cause?: BaseError | Error | undefined + details?: string | undefined docsBaseUrl?: string | undefined docsPath?: string | undefined docsSlug?: string | undefined metaMessages?: string[] | undefined -} & ( - | { - cause?: undefined - details?: string | undefined - } - | { - cause: BaseError | Error | undefined - details?: undefined - } -) +} export type BaseErrorType = BaseError & { name: 'ViemError' } export class BaseError extends Error { diff --git a/src/errors/request.ts b/src/errors/request.ts index a824ff0af6..c9ec1cfbeb 100644 --- a/src/errors/request.ts +++ b/src/errors/request.ts @@ -16,18 +16,21 @@ export class HttpRequestError extends BaseError { constructor({ body, + cause, details, headers, status, url, }: { body?: { [x: string]: unknown } | { [y: string]: unknown }[] | undefined + cause?: Error | undefined details?: string | undefined headers?: Headers | undefined status?: number | undefined url: string }) { super('HTTP request failed.', { + cause, details, metaMessages: [ status && `Status: ${status}`, diff --git a/src/utils/rpc/http.test.ts b/src/utils/rpc/http.test.ts index bbb26996d0..dab97d2519 100644 --- a/src/utils/rpc/http.test.ts +++ b/src/utils/rpc/http.test.ts @@ -303,6 +303,27 @@ describe('request', () => { ) }) + test('fetch error', async () => { + vi.stubGlobal('fetch', () => { + throw new Error('foo', { cause: new Error('bar') }) + }) + + const client = getHttpRpcClient(anvilMainnet.rpcUrl.http) + + try { + await client.request({ + body: { + method: 'eth_getBlockByNumber', + params: [numberToHex(anvilMainnet.forkBlockNumber), false], + }, + }) + } catch (error) { + expect((error as Error).cause).toMatchInlineSnapshot('[Error: foo]') + } + + vi.unstubAllGlobals() + }) + // TODO: This is flaky. test.skip('timeout', async () => { const client = getHttpRpcClient(anvilMainnet.rpcUrl.http) @@ -371,12 +392,12 @@ describe('http (batch)', () => { ).toMatchInlineSnapshot(` [ { - "id": 89, + "id": 91, "jsonrpc": "2.0", "result": "anvil/v0.2.0", }, { - "id": 90, + "id": 92, "jsonrpc": "2.0", "result": "anvil/v0.2.0", }, @@ -397,7 +418,7 @@ describe('http (batch)', () => { ).toMatchInlineSnapshot(` [ { - "id": 92, + "id": 94, "jsonrpc": "2.0", "result": "anvil/v0.2.0", }, @@ -406,7 +427,7 @@ describe('http (batch)', () => { "code": -32602, "message": "Odd number of digits", }, - "id": 93, + "id": 95, "jsonrpc": "2.0", }, ] @@ -423,7 +444,7 @@ describe('http (batch)', () => { ).toMatchInlineSnapshot(` [ { - "id": 95, + "id": 97, "jsonrpc": "2.0", "result": "anvil/v0.2.0", }, @@ -432,7 +453,7 @@ describe('http (batch)', () => { "code": -32601, "message": "Method not found", }, - "id": 96, + "id": 98, "jsonrpc": "2.0", }, ] diff --git a/src/utils/rpc/http.ts b/src/utils/rpc/http.ts index c4a8342fbe..963ffbd846 100644 --- a/src/utils/rpc/http.ts +++ b/src/utils/rpc/http.ts @@ -140,7 +140,7 @@ export function getHttpRpcClient( if (err instanceof TimeoutError) throw err throw new HttpRequestError({ body, - details: (err as Error).message, + cause: err as Error, url, }) }