In Playwright, why does context.addCookies() reject a cookie that has only a name and a value?
answer
- A cookie needs a place to live
- Either url, or domain plus path
- url fills in domain, path, secure
- Expires is seconds, not milliseconds
- Add before the navigation, not after
basics
~10 sBecause Playwright cannot place a cookie without knowing where it belongs. Every entry needs a url, or both domain and path, and passing neither throws: Cookie should have a url or a domain/path pair.
solid answer
~50 sA cookie only means something scoped to a host and a path, so `context.addCookies()` demands that scope up front. Each object needs `name` and `value` plus either a `url` or both `domain` and `path`; passing neither throws `Cookie should have a url or a domain/path pair`, and passing `url` together with `domain` or `path` is rejected too. When you pass `url`, Playwright derives `domain`, `path` and the `secure` flag from it — which is also why a cookie cannot be added for `about:blank` or a `data:` URL. `expires` is a Unix time in **seconds**; omit it and you get a session cookie that dies with the context. Add cookies before the navigation that should send them. In Playwright 1.63, `context.cookies()` reads the jar back, including `httpOnly` cookies, and `context.clearCookies()` takes `name`, `domain` and `path` filters.
code
typescript · 9 linesawait context.addCookies([
{ name: 'currency', value: 'EUR', url: 'https://hotels.example/' },
{ name: 'tenant', value: 'acme', domain: '.hotels.example', path: '/', sameSite: 'Lax' },
]);
await page.goto('https://hotels.example/search');
const jar = await context.cookies('https://hotels.example/search');
console.log(jar.map(c => c.name));go deeper
Learn the required shape by heart: name, value, and either a url or both domain and path. Most first attempts fail because only name and value were passed.
Explain why the scope is mandatory, what a url form fills in for you, and why cookies must be added before the navigation that should send them.
Talk about the failure modes you have debugged: a host mismatch on a subdomain, expires in milliseconds, or an assertion written against document.cookie for an httpOnly cookie.
Decide where injected client state belongs in a suite at all, and how much of a test's starting state should be built by the browser versus written straight into the context.
## Why scope is mandatory A cookie is not a free-floating key/value pair: the browser stores it against a host and a path, and only sends it back to requests that match. `context.addCookies()` writes straight into the context's jar, so it has to know that scope before it can write anything. Playwright validates each entry and throws `Cookie should have a url or a domain/path pair` when the scope is missing, rather than guessing an origin you never named. ## The rules the API enforces 1. `name` and `value` are always required. 2. Either `url`, **or** both `domain` and `path` — one of the two forms, never half of the second. 3. `url` is mutually exclusive with `domain` and with `path`: supplying both forms throws `Cookie should have either url or domain`. 4. `url` may not be `about:blank` or a `data:` URL, because neither can own a cookie. 5. `expires`, when present, is a positive Unix timestamp **in seconds** (or `-1`), not milliseconds. ## What the two forms give you | Form | You write | Playwright derives | Best for | |---|---|---|---| | `url` | `url: 'https://hotels.example/'` | `domain`, `path` and `secure` from the URL | the common case, one origin | | `domain` + `path` | `domain: '.hotels.example', path: '/'` | nothing — you control both | subdomain-wide cookies, a narrow path | A leading dot on `domain` is what makes a cookie apply to subdomains as well, which matters on a multi-tenant booking site where `acme.hotels.example` and `zenith.hotels.example` must both see a tenant cookie. The optional fields are the ones the platform defines: `httpOnly`, `secure` and `sameSite` as `'Strict'`, `'Lax'` or `'None'`. ## Timing: before the request, not after Cookies affect requests that have not been sent yet. `await context.addCookies(...)` before the navigation that must carry them; adding them afterwards changes nothing about the response you already have, and the app has already rendered from whatever the server returned. Because cookies live on the context, every page in the context sees them immediately — you do not repeat the call per page. ## Reading and removing - `context.cookies()` returns the whole jar; pass one or more URLs to get only the cookies that would be sent to them. - The returned objects include `httpOnly` cookies, which page scripts cannot read at all — a useful way to assert on a session cookie that `document.cookie` will never show. - `context.clearCookies()` empties the jar, and in current Playwright releases it accepts `name`, `domain` and `path` filters, each a string or a `RegExp`, so you can drop one cookie and keep the rest. ## The mistakes that produce flaky tests - Setting a cookie with no `expires` and expecting it to survive the context: it will not, and it does not need to, because the context is discarded anyway. - Passing `expires` in milliseconds, which either lands far in the future or trips the validation ceiling. - Writing a cookie for `hotels.example` and navigating to `www.hotels.example`, then wondering why the server never received it — the host must match, dot prefix included. - Adding the cookie after `goto`, and asserting on a page rendered without it. ## Where this sits in a suite Injecting cookies is the mechanical half of starting a test from a known client state; it pairs with an init script for anything that lives in Web Storage rather than in the cookie jar. Keep the values a test needs close to the test, and keep the shape correct — the validation errors above are the first thing a reviewer should recognise from a stack trace.
- How would you assert on a session cookie the application marks httpOnly?Read it through `context.cookies()`, which returns the browser's jar including `httpOnly` entries and their `secure`, `sameSite` and `expires` fields. Page-side `document.cookie` cannot see such a cookie at all, so an assertion written in `page.evaluate` would fail for the wrong reason.
- When would you clear a single cookie instead of opening a new context?When the test is about the transition — checking that the booking app falls back to anonymous pricing after one cookie disappears, while the rest of the session stays intact. `context.clearCookies({ name: 'currency' })` does exactly that; for a clean slate a new context is simpler and cheaper.
saying these in an interview costs you the question
- Passes only name and value and expects it to work
- Supplies url and domain together on the same cookie
- Gives expires in milliseconds like Date.now()
- Adds the cookie after the navigation that needed it
- Assumes a cookie without expires survives the context
- Thinks document.cookie can read an httpOnly cookie