trpc/trpc · critical · Error

No fetch implementation found

Error message

No fetch implementation found

What it means

The tRPC client needs a `fetch` implementation to perform HTTP requests. getFetch() checks, in order: an explicitly passed `customFetchImpl`, then `window.fetch`, then `globalThis.fetch`. If none of these is a function, the client cannot make HTTP calls and throws. This is a startup/runtime environment check, not a network error.

Source

Thrown at packages/client/src/getFetch.ts:22

const isFunction = (fn: unknown): fn is AnyFn => typeof fn === 'function';

export function getFetch(
  customFetchImpl?: FetchEsque | NativeFetchEsque,
): FetchEsque {
  if (customFetchImpl) {
    return customFetchImpl as FetchEsque;
  }

  if (typeof window !== 'undefined' && isFunction(window.fetch)) {
    return window.fetch as FetchEsque;
  }

  if (typeof globalThis !== 'undefined' && isFunction(globalThis.fetch)) {
    return globalThis.fetch as FetchEsque;
  }

  throw new Error('No fetch implementation found');
}

View on GitHub (pinned to acff82332d)

Solutions

  1. Upgrade to Node.js 18+ (or any runtime with a global fetch).
  2. Pass an explicit fetch to the link via `fetch` option, e.g. `httpBatchLink({ url, fetch })` or `createTRPCClient({ links: [...], fetch })`.
  3. Install `undici` / `cross-fetch` and set `globalThis.fetch = undici.fetch` before importing the client.
  4. In tests, configure the test env to provide a fetch (jsdom + `whatwg-fetch`, or vitest's `environment: 'jsdom'` with fetch enabled).

Example fix

// before
import { createTRPCClient, httpBatchLink } from '@trpc/client';
const client = createTRPCClient({ links: [httpBatchLink({ url: '/api/trpc' })] });
// throws on Node 16

// after
import { fetch } from 'undici';
const client = createTRPCClient({
  links: [httpBatchLink({ url: '/api/trpc', fetch })],
});
Defensive patterns

Strategy: validation

Validate before calling

// Run before creating the client
function resolveFetch(explicit?: typeof fetch): typeof fetch | undefined {
  if (explicit) return explicit;
  const g = globalThis as { fetch?: typeof fetch; window?: { fetch?: typeof fetch } };
  return g.fetch ?? g.window?.fetch;
}
const fetchImpl = resolveFetch(myOpts.fetch);
if (!fetchImpl) throw new Error('Configure a fetch polyfill for this runtime');

Type guard

const hasFetch = (): fetch is typeof fetch =>
  typeof globalThis !== 'undefined' &&
  typeof (globalThis as { fetch?: unknown }).fetch === 'function';

Try / catch

try {
  const client = createTRPCClient({ links: [httpBatchLink({ url: '/api/trpc', fetch: fetchImpl })] });
} catch (e) {
  if (e instanceof Error && e.message === 'No fetch implementation found') {
    // install polyfill and retry, or surface a clear env error
  }
  throw e;
}

Prevention

When it happens

Trigger: Creating a client with httpLink/httpBatchLink/httpBatchStreamLink and issuing any query or mutation inside a runtime that exposes no global `fetch`. Also triggered when constructing the link eagerly resolves the fetcher before any request is sent in such an environment.

Common situations: Running tRPC client on Node.js < 18 (no built-in fetch), Jest/jsdom configs that don't polyfill fetch, old React Native engines, edge runtimes with fetch disabled, or SSR entry points that import the client before the runtime provides fetch.

Related errors


AI-assisted analysis of trpc/trpc@acff82332d (2026-08-12). Data as JSON: /api/errors/c8a8d5cc913b185f. Report an issue: GitHub.