microsoft/playwright · error · Error

Route is already handled!

Error message

Route is already handled!

What it means

A network Route can only be resolved once. The RouteDispatcher._checkNotHandled() method flips an internal _handled flag the first time any of continue/fulfill/abort/redirectNavigationRequest is invoked, and throws on every subsequent call. The browser-side route handler is single-shot by design: a request that has already been continued, fulfilled, or aborted cannot be touched again.

Source

Thrown at packages/playwright-core/src/server/dispatchers/networkDispatchers.ts:143

    return { sizes: await this._object.sizes(progress) };
  }
}

export class RouteDispatcher extends Dispatcher<Route, channels.RouteChannel, RequestDispatcher> implements channels.RouteChannel {
  _type_Route = true;

  private _handled = false;

  constructor(scope: RequestDispatcher, route: Route) {
    super(scope, route, 'Route', {
      // Context route can point to a non-reported request, so we send the request in the initializer.
      request: scope
    });
  }

  private _checkNotHandled() {
    if (this._handled)
      throw new Error('Route is already handled!');
    this._handled = true;
  }

  async continue(params: channels.RouteContinueParams, progress: Progress): Promise<channels.RouteContinueResult> {
    // Note: progress is ignored because this operation is not cancellable and should not block in the browser anyway.
    this._checkNotHandled();
    await progress.race(this._object.continue({
      url: params.url,
      method: params.method,
      headers: params.headers,
      postData: params.postData,
      isFallback: params.isFallback,
    }));
  }

  async fulfill(params: channels.RouteFulfillParams, progress: Progress): Promise<void> {
    // Note: progress is ignored because this operation is not cancellable and should not block in the browser anyway.
    this._checkNotHandled();

View on GitHub (pinned to c8fc3bf8d3)

Solutions

  1. Add `return` immediately after every route.fulfill()/route.abort()/route.continue() call so no second call can execute.
  2. Structure the handler as a single if/else-if/else chain where exactly one branch resolves the route.
  3. If multiple handlers match, narrow URL patterns (glob/regex) so only one matches each request.
  4. When chaining route.fetch() to inspect the response, call route.fulfill() with that response — do not also call route.continue().

Example fix

// before
await page.route('**/api', route => {
  if (cached) route.fulfill({ body: cached });
  route.continue(); // throws if cached was true
});
// after
await page.route('**/api', route => {
  if (cached) return route.fulfill({ body: cached });
  return route.continue();
});
Defensive patterns

Strategy: validation

Validate before calling

// Structure the handler so exactly one route method runs and returns.
await page.route('**/api', async route => {
  const req = route.request();
  if (req.method() !== 'GET')
    return route.continue();          // single call + return
  const cached = cache.get(req.url());
  if (cached)
    return route.fulfill({ body: cached });
  return route.continue();
});

Try / catch

// If you cannot guarantee single-call structure, swallow the 'already handled' error.
try {
  await route.fulfill({ body: 'ok' });
} catch (e) {
  if (!(e instanceof Error) || !e.message.includes('already handled')) throw e;
  // route was already resolved elsewhere; safe to ignore
}

Prevention

When it happens

Trigger: Calling route.fulfill() then route.continue() on the same route; calling route.abort() after route.continue(); forgetting a `return` after route.fulfill() in a page.route() handler so execution falls through to a second handler call; two page.route() handlers matching the same URL where both call a route method.

Common situations: Handler written as `if (cond) route.fulfill({...}); route.continue();` without an else/return; calling a route method inside a loop or recursion that re-enters; mixing context.route() with page.route() on overlapping URL patterns so both fire for one request.

Related errors


AI-assisted analysis of microsoft/playwright@c8fc3bf8d3 (2026-08-12). Data as JSON: /api/errors/4777bd4325f9dc5c. Report an issue: GitHub.