skip to content

A Selenium suite calls addCookie before navigating and every test throws InvalidCookieDomainException — why, and what is the fix?

level: seniorimportance: must knowfreq 70%

answer

  1. The browser must already be somewhere
  2. A blank start page has no host
  3. Cookies belong to the loaded document's address
  4. Cheap same-origin URL, then seed, then reload
  5. Parent domain accepted, sibling host refused

basics

~20 s

WebDriver can only set a cookie for the document the browser currently holds. A fresh session sits on a blank page with no host, so there is nothing for the cookie to belong to and addCookie rejects it. Navigate to the target origin first.

solid answer

~40 s

The driver's cookie store is scoped to the address of the document currently loaded. A brand-new session sits on a blank start page that has no host, so the first `addCookie` has no domain to attach to and Selenium raises `InvalidCookieDomainException`. The same error appears when the `Cookie` carries an explicit `domain` the loaded page is not within — a sibling host such as `reports.stmartins.test` while the browser is on `rota.stmartins.test`. The fix is ordering: `driver.get("https://rota.stmartins.test/favicon.ico")` or any cheap URL on that origin, then `addCookie`, then navigate to the page the test really wants. Leave `domain` unset so the browser assigns the current host, or set it to a shared parent such as `.stmartins.test` when the cookie must cover several subdomains.

code

java · 11 lines
java
WebDriver driver = new ChromeDriver();

driver.get("https://rota.stmartins.test/favicon.ico");

driver.manage().addCookie(
    new Cookie.Builder("rota_view", "week")
        .path("/")
        .isSecure(true)
        .build());

driver.get("https://rota.stmartins.test/wards/icu");

go deeper

for a junior

Remember the ordering rule: navigate to the site first, then add cookies. If the first call in a fresh session is addCookie, expect it to fail.

for a middle

Explain that the cookie API is scoped to the address of the loaded document, and say which domain values that address accepts — its own host and a parent domain, not a sibling.

for a senior

Diagnose the whole-suite failure from the symptom: one shared setup hook seeding before navigation. Show the cheap same-origin landing URL and the reload that makes the console honour the seeded value.

for a principal

Decide where seeding belongs in the harness so the ordering cannot be got wrong again, and what the suite pays per test for the extra navigation across every environment it runs in.

## What the error is actually saying `InvalidCookieDomainException` is Selenium's mapping of the WebDriver protocol's **invalid cookie domain** error. It means the remote end was asked to store a cookie that does not belong to the document the browser is currently showing. It is not a network failure, not a certificate problem, and not a defect in the shift-rota console — the request never reached the site. The rule underneath is simple: the driver's cookie API operates on the cookies **associated with the address of the current browsing context's active document**. Adding a cookie means adding it *for that address*. If the address cannot host the cookie, there is nothing sensible to do but refuse. ## Why a fresh session is the classic trigger A driver that has just started has not been told to load anything. The browser is sitting on its blank start page, whose address carries no host at all. There is no origin for `rota_view` to belong to, so the very first `addCookie` in the suite fails — and because most suites seed cookies in a shared setup hook, *every* test fails identically, which is what makes the symptom look like an environment outage rather than an ordering bug. | Situation when addCookie runs | Outcome | |---|---| | Blank start page, no navigation yet | `InvalidCookieDomainException` | | On `https://rota.stmartins.test/...`, cookie has no `domain` | stored for `rota.stmartins.test` | | On `https://rota.stmartins.test/...`, `domain(".stmartins.test")` | stored for the parent domain | | On `https://rota.stmartins.test/...`, `domain("reports.stmartins.test")` | `InvalidCookieDomainException` | | On `https://rota.stmartins.test/...`, `domain("stmartins.example")` | `InvalidCookieDomainException` | ## The fix is an ordering rule 1. **Land on the target origin.** Any URL on it will do; the response does not even have to be a real page. A small static asset or a health path costs a fraction of the full console's load. 2. **Add the cookies.** Now the browser holds a document the cookie can belong to. 3. **Navigate to the page under test**, or call `navigate().refresh()` if you are already on it, so the console sends the seeded cookie on its next request. Step 3 matters more than people expect. A cookie added after a page has rendered does not retroactively change that page: the markup is already built and the scripts have already run. Without the reload, the assertion looks at a view produced before the cookie existed. ## Choosing the domain attribute - **Leave it unset** in most tests. The browser assigns the host of the current document, which is what you wanted anyway, and there is nothing to keep in sync when the environment hostname changes. - **Set a parent domain** — `.stmartins.test` — only when the cookie genuinely has to be visible to more than one subdomain of the rota system. A parent domain of the current host is accepted; the leading dot is a historical spelling browsers normalise away. - **Never set a sibling or unrelated host.** `reports.stmartins.test` is not a domain the page on `rota.stmartins.test` is within, and the driver refuses it. So does an entirely different site. - **Do not try to escape the rule with the `Cookie` object.** Setting `domain(...)` is not an alternative to navigating; the check is against the loaded document either way. ## Where the cheap landing page comes from The usual pick on the shift-rota console is a static asset such as `/favicon.ico`, or an unauthenticated health endpoint. Two properties matter: it must be on the **same origin** as the pages under test, and it should be cheap enough that paying for it in every test is invisible. A URL that 404s is perfectly acceptable — the browser still has a document on that origin afterwards, which is all the cookie API needs. What does not work is loading a *different* host and expecting the cookie to carry over. ## Failures that look like this but are not - **The cookie is accepted and then missing.** No exception, but `getCookieNamed` returns `null`. That is the browser silently declining to store it, which is a different diagnosis entirely — check the expiry, the path and the Secure flag. - **The cookie is stored but the console ignores it.** The seeding worked; the page was never reloaded, or the value is not what the application expects. - **The whole session fails to start.** If the driver never got a session, no cookie call ran at all; read the stack trace from the top rather than from the cookie line.

  • The suite must seed one cookie for rota.stmartins.test and one for reports.stmartins.test. How do you do it?
    Either visit each host and seed its cookie there, or set `domain(".stmartins.test")` on a single cookie if both subdomains genuinely need the same value. There is no way to seed a cookie for a host the browser is not on: the cookie API always works against the address of the loaded document, so two hosts means two visits.
  • Does setting the Cookie's domain attribute remove the need to navigate first?
    No. The driver validates the requested domain against the document the browser currently holds, so on a blank start page every domain is invalid. The attribute only widens or narrows the cookie within the origin you are already on — it cannot reach a host the browser has not loaded.
  • Why does a suite that seeds a cookie correctly still show the console's onboarding overlay?
    The page was rendered before the cookie existed. Adding a cookie does not re-run the page's markup or scripts, so the view on screen still reflects the state at load time. Follow the seeding with `navigate().refresh()` or a fresh `get(...)` to the page under test.

saying these in an interview costs you the question

  • Blames the site or the environment for the exception
  • Thinks setting the domain attribute avoids navigating first
  • Tries to seed a cookie for a completely different host
  • Seeds the cookie but never reloads the page
  • Adds a Thread.sleep, expecting the cookie to land later