Skip to content

url: setters overwrite URL changes made during value coercion #67345

Open

Description

@zloirock

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions