In Playwright, what happens to a request that routeFromHAR() finds no matching entry for?
answer
- Two reasons a request is not answered
- Scope filter versus missing entry
- Default is the strict one
- One value lets it pass through
- Aborted, not answered with 404
basics
~20 sBy default it is aborted, so the application sees a failed request rather than a response. Passing notFound: 'fallback' instead lets the request continue to the next matching route handler or to the real network.
solid answer
~40 sIn Playwright 1.63 the `notFound` option on `routeFromHAR()` defaults to `'abort'`: a request the archive cannot answer is killed, and the app's `fetch` rejects exactly as if the network had failed. Setting `notFound: 'fallback'` makes the miss fall through instead, so a later route handler or the real server answers it. The `url` option is a different filter and is often confused with this: a request that does not match `url` is never consulted against the archive at all, so it reaches the network regardless of `notFound`. Abort is the hermetic choice — an incomplete recording of the forecast API fails loudly in CI. Fallback suits a partial archive, where you replay `**/api/forecast**` and let page HTML, fonts and analytics go to the dev server as usual.
code
typescript · 11 linesimport { test, expect } from '@playwright/test';
test('only the forecast API is replayed', async ({ context, page }) => {
await context.routeFromHAR('har/forecast.har', {
url: '**/api/forecast**',
notFound: 'fallback',
});
await page.goto('/dashboard');
await expect(page.getByRole('heading', { name: 'Oslo' })).toBeVisible();
});go deeper
Remember that a request the archive cannot answer is aborted, not passed through, and that notFound is the option controlling it. Recall the two values, abort and fallback.
Explain both no-match paths: filtered out by url versus in scope but absent, and why only the second is governed by notFound. Describe what an abort looks like inside the page.
Argue the tradeoff for a real suite: abort buys a hermetic run and loud gaps, fallback buys convenience at the cost of silent calls to the live service from CI.
Set the policy for the organisation: whether unrecorded traffic in CI is a build failure by default, and how that choice interacts with the network egress you allow test runners.
## Two different kinds of "no match" `routeFromHAR()` can decline to answer a request for two quite different reasons, and mixing them up is the classic source of confusion. 1. **The request was filtered out by `url`.** If you passed `url: '**/api/forecast**'`, a request for `/styles.css` is not the archive's business. The HAR route never even looks it up; the request continues to any other handler and then to the network. 2. **The request was in scope but absent from the archive.** The URL matched `url` (or you passed no `url` at all, which means everything), yet no entry in the file has that method and URL. This is the only case `notFound` governs. ## The default: `notFound: 'abort'` The request is aborted. In the browser this surfaces the way a genuine network failure does: `fetch` rejects, `XMLHttpRequest` errors, an image renders broken. The weather dashboard's forecast panel shows whatever it shows when the API is unreachable — usually an empty or error state. That is a deliberate, useful default: - It keeps the run **hermetic**. Nothing silently escapes to the third-party forecast provider from CI, so a green run really did prove the app works against the recording. - It makes an **incomplete recording visible**. A new call the app started making after the archive was captured fails instead of quietly working on a developer's machine and nowhere else. - It fails at the point of the missing call, not three assertions later. The cost is that the archive must cover everything in scope. If you record only the forecast API but leave `url` unset, the page's own HTML and assets are in scope too, so the very first navigation is aborted and the test dies on `page.goto()`. ## The alternative: `notFound: 'fallback'` The miss is handed on: Playwright continues to the next matching route handler, and if none answers it, to the real network. This is what you want when the archive is deliberately partial — replay the third-party API, let your own dev server serve the app. ```ts await context.routeFromHAR('har/forecast.har', { notFound: 'fallback' }); ``` With `fallback`, a missing entry never fails the test on its own; the test only fails if the application misbehaves. That is friendlier during development and weaker as a guarantee, because a request that quietly reached the real forecast API leaves no obvious trace in the test result. ## Choosing between them | you want | `url` | `notFound` | result | |---|---|---|---| | fully hermetic run | unset | `'abort'` | the archive must contain every request, navigation included | | replay one API only | `'**/api/forecast**'` | `'abort'` | a gap in the recorded API fails; everything else is served normally | | forgiving partial replay | unset or narrow | `'fallback'` | anything unrecorded reaches the network | The middle row is usually the right default for a suite: scope the archive with `url`, keep `abort` so gaps in the endpoints you actually recorded are loud, and let the rest of the page load normally. ## Diagnosing a miss When a replayed run behaves as if the API is down, work in this order: 1. Confirm the request is in scope — does its URL match the `url` pattern you passed? 2. Compare the exact URL the app requests with the URLs in the archive. A cache-busting `?t=` value, a different `baseURL` or an extra query parameter changes the lookup key and produces a miss. 3. Check the method. A `POST` will not be answered by a recorded `GET` of the same path. 4. Temporarily switch to `notFound: 'fallback'`. If the panel fills in, the request was reaching the real service and the archive is missing that entry. ## What it is not `notFound` does not fail the test by itself, and it does not turn a miss into an HTTP error status: an aborted request has no response at all, which is different from a recorded `404`. It also does not affect requests already answered from the archive, and it has nothing to say about whether the archive is stale — a recorded-but-outdated response is a match, and replay serves it happily.
- Why would you deliberately keep notFound at its default of 'abort'?Because it makes an incomplete archive fail loudly. With abort, a call the app added since the recording cannot quietly reach the real forecast provider from CI, so a green run genuinely proves the app worked against the fixture rather than against a live third party.
- What happens to a request that does not match the url option?Nothing from the archive's point of view. It is out of scope, so it is not looked up and notFound never applies to it: it continues to any other route handler and then to the real network. That is how you replay one API while the rest of the page loads normally.
- Does an aborted request fail the test on its own?No. The abort surfaces inside the page as a network failure, and the test fails only if that makes an assertion fail or a navigation throw. A background call that the UI ignores can be aborted without the test noticing at all.
saying these in an interview costs you the question
- Thinks an unmatched request reaches the network by default
- Believes notFound: 'fallback' makes the test fail on a miss
- Confuses the url scope filter with the notFound policy
- Assumes an aborted request arrives as an HTTP 404
- Thinks a miss stops the whole test immediately