sveltejs/kit · error · Error

Cannot use relative URL (${info}) with global fetch — use `e

Error message

Cannot use relative URL (${info}) with global fetch — use `event.fetch` instead: https://svelte.dev/docs/kit/web-standards#fetch-apis

What it means

In dev, SvelteKit patches `globalThis.fetch` to catch a common mistake: calling global `fetch` with a relative URL (e.g. `fetch('/api/data')`). Relative URLs are invalid for global fetch because there is no request context to resolve them against; inside server code you must use `event.fetch`, which resolves relative URLs against the current request.

Source

Thrown at packages/kit/src/exports/vite/dev/index.js:67

	/** @type {AsyncLocalStorage<{ event: RequestEvent, config: any, prerender: PrerenderOption }>} */
	const async_local_storage = new AsyncLocalStorage();

	globalThis.__SVELTEKIT_TRACK__ = (label) => {
		const context = async_local_storage.getStore();
		if (!context || context.prerender === true) return;

		check_feature(
			/** @type {string} */ (context.event.route.id),
			context.config,
			label,
			svelte_config.adapter
		);
	};

	const fetch = globalThis.fetch;
	globalThis.fetch = (info, init) => {
		if (typeof info === 'string' && !SCHEME.test(info)) {
			throw new Error(
				`Cannot use relative URL (${info}) with global fetch — use \`event.fetch\` instead: https://svelte.dev/docs/kit/web-standards#fetch-apis`
			);
		}

		return fetch(info, init);
	};

	write_tsconfig(svelte_config, root);

	/** @type {ManifestData} */
	let manifest_data;
	/** @type {SSRManifest} */
	let manifest;

	/** @type {Error | null} */
	let manifest_error = null;

	const runner = get_runner(vite, vite_dev_server);

View on GitHub (pinned to 03f1687fe6)

Solutions

  1. Use `event.fetch` (or `fetch` from the load/action/`RequestEvent` context) instead of global fetch
  2. Prefix the URL with an absolute origin, e.g. `fetch(url.origin + '/api/data')` using `event.url`
  3. Pass the event/fetch into shared helper functions so relative URLs resolve correctly
  4. Restructure to call internal functions directly instead of fetching your own API

Example fix

// before
export async function load() {
  const res = await fetch('/api/data');
}
// after
export async function load({ fetch }) {
  const res = await fetch('/api/data');
}
Defensive patterns

Strategy: type-guard

Validate before calling

function assertAbsoluteUrl(url) {
  if (/^\/[^^]*|^\?/.test(url)) {
    throw new TypeError(`Use event.fetch for relative URLs: ${url}`);
  }
}

Type guard

function isAbsoluteUrl(url) {
  return typeof url !== 'string' || !!SCHEME_LIKE.test(url) || URL.canParse(url) && new URL(url).origin !== 'null';
}

Try / catch

try {
  const res = await globalFetch('/api/data');
} catch (err) {
  if (/relative URL.*global fetch/.test(err.message)) {
    console.error('Switch to event.fetch in server code');
  }
  throw err;
}

Prevention

When it happens

Trigger: Any server-side code (load function without event.fetch, +server.ts handler, hooks, instrumentation) calling `fetch('/path')` with a string lacking a scheme during `vite dev`.

Common situations: Code copied from client components into server load; shared data-fetching modules that use global fetch; unit-tested fetch wrappers that worked with a mocked base URL.

Related errors


AI-assisted analysis of sveltejs/kit@03f1687fe6 (2026-09-02). Data as JSON: /api/errors/0894256b00bcd235. Report an issue: GitHub.