In packages/core/src/agent/canister-env/index.ts:
return globalThis.document.cookie
.split(";")
.find(cookie => cookie.trim().startsWith(`${cookieName}=`));
With more than one ic_env cookie on an origin, this resolves whichever the browser happens to list first. If that one names a canister that no longer exists, every call rejects with IC0536 and the app looks like the network is down.
Why several can be present
A cookie is keyed on (name, domain, path), partitioning adds a separate jar, and cookies ignore the port. The writers of ic_env do not agree on any of those:
| writer |
attributes |
mainnet asset canister (skills.internetcomputer.org) |
Partitioned; SameSite=None; Secure; Max-Age=31536000; Path=<certified> |
local gateway (icp network start) |
SameSite=Lax; secure; Max-Age=31536000; Path=<certified> |
| typical Vite dev-server simulation |
SameSite=Lax (no Path, no Max-Age) |
So they accumulate rather than replace, and Max-Age=31536000 lets a stale one persist for a year. Port-insensitivity means a dev server on localhost:5173 and a gateway on localhost:8000 share a jar.
To be precise about evidence: the attributes above are measured. The duplicate-cookie failure was diagnosed against a running local network but I have not reproduced it here, so I am not claiming to know which combination produced it — only that the resolver picks arbitrarily when it happens.
Suggestion
Anything that makes the ambiguity visible or decidable, roughly in order of preference:
- Return all candidates (or expose a
getAllCanisterEnvs) so the caller can choose.
- Prefer the non-partitioned copy, or the most specific
Path — some documented rule rather than list order.
- At minimum,
console.warn when more than one ic_env is present and they disagree. That alone turns an opaque IC0536 into a one-glance diagnosis.
Related: dfinity/icp-cli#702 asks for a shorter Max-Age on the gateway-set cookie, which would reduce how long a stale copy survives but does not make the resolver deterministic.
Implementation note for whoever picks this up: cookieStore.delete silently removes nothing from the partitioned jar unless partitioned: true is passed, so any cleanup helper has to try both.
In
packages/core/src/agent/canister-env/index.ts:With more than one
ic_envcookie on an origin, this resolves whichever the browser happens to list first. If that one names a canister that no longer exists, every call rejects with IC0536 and the app looks like the network is down.Why several can be present
A cookie is keyed on (name, domain, path), partitioning adds a separate jar, and cookies ignore the port. The writers of
ic_envdo not agree on any of those:skills.internetcomputer.org)Partitioned; SameSite=None; Secure; Max-Age=31536000; Path=<certified>icp network start)SameSite=Lax; secure; Max-Age=31536000; Path=<certified>SameSite=Lax(noPath, noMax-Age)So they accumulate rather than replace, and
Max-Age=31536000lets a stale one persist for a year. Port-insensitivity means a dev server onlocalhost:5173and a gateway onlocalhost:8000share a jar.To be precise about evidence: the attributes above are measured. The duplicate-cookie failure was diagnosed against a running local network but I have not reproduced it here, so I am not claiming to know which combination produced it — only that the resolver picks arbitrarily when it happens.
Suggestion
Anything that makes the ambiguity visible or decidable, roughly in order of preference:
getAllCanisterEnvs) so the caller can choose.Path— some documented rule rather than list order.console.warnwhen more than oneic_envis present and they disagree. That alone turns an opaque IC0536 into a one-glance diagnosis.Related: dfinity/icp-cli#702 asks for a shorter
Max-Ageon the gateway-set cookie, which would reduce how long a stale copy survives but does not make the resolver deterministic.Implementation note for whoever picks this up:
cookieStore.deletesilently removes nothing from the partitioned jar unlesspartitioned: trueis passed, so any cleanup helper has to try both.