sveltejs/kit · error · Error

Cannot use `match(...)` inside a service worker, as it depen

Error message

Cannot use `match(...)` inside a service worker, as it depends on the SvelteKit client instance

What it means

match(...) from $app/paths relies on the running SvelteKit client instance to know the app's routes. Service workers run in a separate global scope without that client, so SvelteKit throws if match is invoked inside a ServiceWorkerGlobalScope, offering a null-returning stub otherwise.

Source

Thrown at packages/kit/src/runtime/app/paths/internal/client.js:23

/** @import { RouteId } from '$app/types' */
import { payload } from '../../../client/payload.js';

export const base = payload.base ?? __SVELTEKIT_PATHS_BASE__;
export const assets = payload.assets ?? base ?? __SVELTEKIT_PATHS_ASSETS__;
export const app_dir = __SVELTEKIT_APP_DIR__;
export const hash_routing = __SVELTEKIT_HASH_ROUTING__;

/**
 * We make this configurable per-environment so that it's possible to import `$app/paths`
 * into a service worker without importing the entire client
 * @param {URL | string} _url
 * @returns {Promise<{ [K in RouteId]: { id: K; params: import('$app/types').RouteParams<K>; } }[RouteId] | null>}
 */
// eslint-disable-next-line @typescript-eslint/require-await
export let match_implementation = async (_url) => {
	// @ts-ignore
	if (typeof ServiceWorkerGlobalScope !== 'undefined' && self instanceof ServiceWorkerGlobalScope) {
		throw new Error(
			'Cannot use `match(...)` inside a service worker, as it depends on the SvelteKit client instance'
		);
	}

	return null;
};

/**
 * @param {typeof match_implementation} fn
 */
export function set_match_implementation(fn) {
	match_implementation = fn;
}

View on GitHub (pinned to 03f1687fe6)

Solutions

  1. In the service worker, match URLs yourself (e.g. use the route manifest passed to the sw build, or simple regex/pattern matching)
  2. Keep match() usage in app code only; guard shared modules so it is not called in the sw context
  3. Use $service-worker's `build`, `files`, and `version` exports for caching logic instead of runtime route matching
  4. If the sw just needs to decide which requests to handle, check request.destination/url patterns directly

Example fix

// before (service worker)
import { match } from '$app/paths';
const routes = await match(request.url);
// after
import { build, files, version } from '$service-worker';
const cached = [...build, ...files]; // decide by URL patterns instead
Defensive patterns

Strategy: type-guard

Validate before calling

if (typeof ServiceWorkerGlobalScope !== 'undefined' && self instanceof ServiceWorkerGlobalScope) {
  // do NOT import/call match() here; use $service-worker exports instead
}

Type guard

const inServiceWorker = () =>
  typeof ServiceWorkerGlobalScope !== 'undefined' && self instanceof ServiceWorkerGlobalScope;

Try / catch

let routes = null;
try {
  routes = await match(url);
} catch (e) {
  if (e.message.includes('inside a service worker')) {
    routes = matchUrlManually(url); // sw-local pattern matching
  } else throw e;
}

Prevention

When it happens

Trigger: Importing match from $app/paths (or $service-worker re-exports) and calling it inside a service worker script — detected via `self instanceof ServiceWorkerGlobalScope`.

Common situations: Writing custom service workers that try to reuse app routing helpers, copying client-side path resolution code into sw.js, or sharing a module between app and service worker that calls match at module scope.

Related errors


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