{"record":{"id":"4777bd4325f9dc5c","repo":"microsoft/playwright","slug":"route-is-already-handled-4777bd","errorCode":null,"errorMessage":"Route is already handled!","messagePattern":"Route is already handled!","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/playwright-core/src/server/dispatchers/networkDispatchers.ts","lineNumber":143,"sourceCode":"    return { sizes: await this._object.sizes(progress) };\n  }\n}\n\nexport class RouteDispatcher extends Dispatcher<Route, channels.RouteChannel, RequestDispatcher> implements channels.RouteChannel {\n  _type_Route = true;\n\n  private _handled = false;\n\n  constructor(scope: RequestDispatcher, route: Route) {\n    super(scope, route, 'Route', {\n      // Context route can point to a non-reported request, so we send the request in the initializer.\n      request: scope\n    });\n  }\n\n  private _checkNotHandled() {\n    if (this._handled)\n      throw new Error('Route is already handled!');\n    this._handled = true;\n  }\n\n  async continue(params: channels.RouteContinueParams, progress: Progress): Promise<channels.RouteContinueResult> {\n    // Note: progress is ignored because this operation is not cancellable and should not block in the browser anyway.\n    this._checkNotHandled();\n    await progress.race(this._object.continue({\n      url: params.url,\n      method: params.method,\n      headers: params.headers,\n      postData: params.postData,\n      isFallback: params.isFallback,\n    }));\n  }\n\n  async fulfill(params: channels.RouteFulfillParams, progress: Progress): Promise<void> {\n    // Note: progress is ignored because this operation is not cancellable and should not block in the browser anyway.\n    this._checkNotHandled();","sourceCodeStart":125,"sourceCodeEnd":161,"githubUrl":"https://github.com/microsoft/playwright/blob/c8fc3bf8d31542d59b4d4d9eaab1df93ff541dc6/packages/playwright-core/src/server/dispatchers/networkDispatchers.ts#L125-L161","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Add `return` immediately after every route.fulfill()/route.abort()/route.continue() call so no second call can execute.","Structure the handler as a single if/else-if/else chain where exactly one branch resolves the route.","If multiple handlers match, narrow URL patterns (glob/regex) so only one matches each request.","When chaining route.fetch() to inspect the response, call route.fulfill() with that response — do not also call route.continue()."],"exampleFix":"// before\nawait page.route('**/api', route => {\n  if (cached) route.fulfill({ body: cached });\n  route.continue(); // throws if cached was true\n});\n// after\nawait page.route('**/api', route => {\n  if (cached) return route.fulfill({ body: cached });\n  return route.continue();\n});","handlingStrategy":"validation","validationCode":"// Structure the handler so exactly one route method runs and returns.\nawait page.route('**/api', async route => {\n  const req = route.request();\n  if (req.method() !== 'GET')\n    return route.continue();          // single call + return\n  const cached = cache.get(req.url());\n  if (cached)\n    return route.fulfill({ body: cached });\n  return route.continue();\n});","typeGuard":null,"tryCatchPattern":"// If you cannot guarantee single-call structure, swallow the 'already handled' error.\ntry {\n  await route.fulfill({ body: 'ok' });\n} catch (e) {\n  if (!(e instanceof Error) || !e.message.includes('already handled')) throw e;\n  // route was already resolved elsewhere; safe to ignore\n}","preventionTips":["Always `return` immediately after any route.continue/fulfill/abort call.","Write route handlers as a single if/else-if/else chain with one resolving branch.","Avoid overlapping page.route() and context.route() URL patterns."],"tags":["network","routing","api-misuse"],"backgroundTag":null,"analyzedSha":"c8fc3bf8d31542d59b4d4d9eaab1df93ff541dc6","analyzedAt":"2026-08-12T07:26:36.950Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}