HMR CSS observer throws Invalid URL for dynamically inserted image preload without href · Issue #46164 · oven-sh/bun · GitHub
Skip to content

HMR CSS observer throws Invalid URL for dynamically inserted image preload without href #46164

Open

Description

@Teyik0

What version of Bun is running?

Reproduced on both:

  • 1.4.3+c6da4a4d3
  • 1.4.2+744846f84

What platform is your computer?

Darwin 25.3.0 arm64 arm (uname -mprs)

Browser: Chromium 153.0.8010.12, driven with Playwright.

What steps can reproduce the bug?

Create these four files in an empty directory. No dependencies or framework are needed.

server.ts:

import page from "./index.html";

Bun.serve({
  port: 34143,
  routes: { "/": page },
  development: { hmr: true, console: true },
});

index.html:

<!doctype html>
<html>
  <head>
    <link rel="stylesheet" href="./style.css" />
    <script type="module" src="./client.ts"></script>
  </head>
  <body>
    <button id="reproduce">Add responsive image preload</button>
  </body>
</html>

style.css:

body { font-family: sans-serif; }

client.ts:

document.querySelector("#reproduce")!.addEventListener("click", () => {
  const link = document.createElement("link");
  link.rel = "preload";
  link.as = "image";
  link.imageSrcset = "/small.png 480w, /large.png 800w";
  link.imageSizes = "100vw";
  document.head.appendChild(link);
});

Run bun server.ts, open http://localhost:34143/, and click the button. The image files do not need to exist: the exception is thrown by the CSS HMR observer when the link is inserted, before any image response matters.

I verified this minimal reproduction in a fresh browser context separately under Bun 1.4.2 and 1.4.3. Both produce the same exception and error overlay.

What is the expected behavior?

The CSS HMR observer should ignore non-stylesheet links, including responsive image preloads that use imagesrcset without an href. Adding such a link should not throw or open the runtime error overlay.

What do you see instead?

TypeError: Failed to construct 'URL': Invalid URL
    at EZ (http://localhost:34143/_bun/client/index-00000000059b9630.js:1:785)
    at MutationObserver.<anonymous> (http://localhost:34143/_bun/client/index-00000000059b9630.js:1:585)

The stack above is from 1.4.2. On 1.4.3, the minified function is named Le rather than EZ, at the same :1:785 call site followed by MutationObserver at :1:585. The generated bundle identifier can differ between runs.

Additional information

The issue originally appeared during client-side navigation in a React 19.3.1 application, where responsive image preloads can be inserted into the head. The reproduction above removes React and the framework entirely.

In src/runtime/bake/client/css-reloader.ts, headObserver passes every added HTMLLinkElement to maybeAddCssLink. That function immediately evaluates:

const pathname = new URL(link.href).pathname;

For the preload above, link.href is an empty string, so the URL constructor throws. The initial document scan filters for head > link[rel=stylesheet], but the added-node path does not apply that filter.

A possible fix is to check the link's relation and a nonempty href before parsing the URL. This report does not depend on a particular fix; the regression test should cover dynamically inserted image preloads without an href while CSS HMR is active.

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