Skip to content

node:http fails to link from a cold auto-optimize cache: js_bun_http_response_snapshot_json is web-fetch-gated but referenced unconditionally #9719

Description

@proggeramlug

Summary

A cold auto-optimize link of any program importing node:http fails with an undefined symbol. Found while validating an unrelated PR; reproduces independently of it, and the causal chain is visible in the source on main.

Undefined symbols for architecture arm64:
  "_js_bun_http_response_snapshot_json", referenced from:
      perry_ext_http::server::bun_server::settle_value in
      libperry_ext_http.a(...perry_ext_http...cgu.10.rcgu.o)
ld: symbol(s) not found for architecture arm64
clang: error: linker command failed with exit code 1
Error: Linking failed

Repro

// http_server.ts
import http from "node:http";
const server = http.createServer((req, res) => { res.end("{}"); });
server.listen(0, () => { console.log("listening"); server.close(); });

perry compile http_server.ts -o http_server from a cold cache (fresh
node_modules/.cache/perry, no warm target/perry-auto-*). A warm cache built
before this masks it, which is part of why it is not visible day to day.

Cause

js_bun_http_response_snapshot_json is defined in
crates/perry-stdlib/src/fetch/bun_server_bridge.rs:79. mod bun_server_bridge
is ungated, but its parent is not — crates/perry-stdlib/src/lib.rs:136:

#[cfg(feature = "web-fetch")]
pub mod fetch;

The auto-optimize rebuild for a node:http program selects

async-runtime, external-http-client-pump, external-http-server-pump,
external-net-tls, external-tls-server, external-ws-pump

which does not include web-fetch, so the whole fetch module — and the
symbol — is compiled out. Meanwhile perry-ext-http's
server/bun_server.rs declares and calls it unconditionally (3 references on
main). Definition and reference disagree about the feature that gates them.

Why CI is green

This is the CI-invisible class recorded in changelog.d/memory around #5112:
plain cargo build / cargo test never link the auto-optimize ext-http path,
so nothing in the normal suites exercises it, and any pre-existing warm
target/perry-auto-* cache hides it from a manual check too. Only a fresh
cold auto-opt link surfaces it.

Fix shapes

  1. Gate perry-ext-http's use of the bridge on the same feature that gates its
    definition, so the two cannot disagree.
  2. Have the driver add web-fetch whenever it selects the http server features
    (widens the build).
  3. Move js_bun_http_response_snapshot_json out of the web-fetch-gated module
    into one that is always compiled when external-http-server-pump is on.

(1) or (3) look right; (2) treats the symptom.

Worth a gate either way: a cold-cache node:http compile-and-run in the tier
that already builds package smokes would have caught this, and would catch the
next instance of the same class.

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