Repository navigation
Description
This issue was drafted with the help of LLMs (Claude and Codex); I reviewed it and verified the reproductions.
Version
v26.13.0. Also reproduced on v20.20.2, v22.23.4 and v24.22.2.
Platform
macOS 26.7, Darwin 25.6.0, arm64
Subsystem
url / WHATWG URL
What steps will reproduce the bug?
Run this directly with Node.js; no dependencies or polyfills are involved:
const url = new URL('https://before.example/old');
url.pathname = {
toString() {
url.href = 'https://after.example/base';
return '/new';
},
};
console.log(url.href);How often does it reproduce? Is there a required condition?
Always for the example above: the assigned object's toString() changes href before returning the new pathname.
What is the expected behavior? Why is that the expected behavior?
https://after.example/new
Web IDL converts the assigned value to the attribute's IDL type before running the attribute's setter steps. The pathname setter then updates the URL's current path. At that point, toString() has already changed the URL to https://after.example/base, so the setter must retain after.example and replace its path with /new.
What do you see instead?
https://before.example/new
The setter restores the hostname from before toString() ran, discarding the change to href made during conversion.
Additional information
Tested regression boundaries:
| Node.js version | Hostname after the assignment |
|---|---|
| 18.15.0 | after.example (expected) |
| 18.16.0 | before.example |
| 19.6.1 | after.example (expected) |
| 19.7.0 | before.example |
| 20.0.0, 20.20.2, 22.23.4, 24.22.2, 26.13.0 | before.example |
The behavior starts with the Ada integration in #46410. In the current JavaScript wrapper, the first argument to bindingUrl.update reads this.#context.href before a later argument performs string conversion of the assigned value. Consequently, the binding receives the URL string captured before the user's toString() ran.
This order remains in this main-branch source snapshot. The runtime results above are from the listed Node.js releases; the main snapshot was inspected separately.
I also reproduced stale-state behavior with the host, hostname and port setters when their value's toString() changes href between an opaque URL and a hierarchical URL.
The same snippet produces the expected output in Chromium 156.0.8078.4, Firefox 157.0 and WebKit 27.2 (Playwright builds), as well as Bun 1.4.4 and Deno 2.9.7.
Activity