Skip to content

riscv64 unofficial builds (v24.18, v24.20): fetch() rejects valid HTTP/1.1 responses — root cause is the llhttp SIMD WASM parser on riscv64; UNDICI_NO_WASM_SIMD=1 fixes it #250

Open

Description

@codemk8

Summary

On the riscv64 unofficial Node builds, fetch() (undici) fails against every HTTPS host with Response does not match the HTTP/1.1 protocol (Invalid header value char) — while the http module, curl, and python all parse the identical responses correctly. Root cause found: undici defaults to the llhttp SIMD WASM parser on all arches except ppc64, and that WASM build misparses valid HTTP/1.1 responses under V8 on riscv64. Setting UNDICI_NO_WASM_SIMD=1 forces the non-SIMD llhttp WASM parser and everything works.

These unofficial tarballs are currently the only way to run Node >= 22 on riscv64 (no official riscv64 builds), so this blocks every Node tool that uses fetch() on RISC-V boards.

Environment

Repro

fetch("https://registry.npmjs.org/pi/latest")
  .then(r => console.log("status", r.status))
  .catch(e => console.log("FETCH ERR:", e.message));
FETCH ERR: Response does not match the HTTP/1.1 protocol (Invalid header value char)

Fails against: registry.npmjs.org, api.github.com, raw.githubusercontent.com, api.deepseek.com.

Root cause (verified)

Node's bundled undici (embedded in the binary) selects its HTTP parser like this:

let useWasmSIMD = process.arch !== "ppc64";            // riscv64 -> SIMD by default
if (process.env.UNDICI_NO_WASM_SIMD === "1") {
  useWasmSIMD = false;
} else if (process.env.UNDICI_NO_WASM_SIMD === "0") {
  useWasmSIMD = true;
}
if (useWasmSIMD) {
  mod = new WebAssembly.Module(require_llhttp_simd_wasm());  // <- broken on riscv64
}
if (!mod) {
  mod = new WebAssembly.Module(require_llhttp_wasm());       // <- works
}

The SIMD llhttp WASM (llhttp_simd-wasm.js, embedded in the binary) misparses valid responses on riscv64. Evidence:

  • UNDICI_NO_WASM_SIMD=1 → fetch() works (200 from registry.npmjs.org, api.github.com; 401 from api.deepseek.com as expected)
  • The same clean response bytes parse correctly under the standalone llhttp-wasm npm package (non-SIMD) on the same machine/Node build
  • Node's http module (C llhttp) parses the identical responses fine
  • WebAssembly in general works on this build (minimal module compiles); the failure is specific to this WASM module (SIMD)
  • System Node 18.19.1 (distro build) fetch() works

This looks like a V8 riscv64 WASM-SIMD codegen issue with this llhttp WASM build (or an llhttp SIMD build incompatibility), not a server/network problem.

Workaround

export UNDICI_NO_WASM_SIMD=1   # forces the working non-SIMD llhttp parser

Verified to unblock fetch() in Node itself, in pi (which bundles its own undici — same env var applies), and should apply to any Node >= 22 tool on riscv64.

Expected

A fix or a build-time default of useWasmSIMD = false on riscv64 for these unofficial builds, and/or a fix in the llhttp SIMD WASM / V8 riscv64 WASM-SIMD codegen.

Activity

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

Metadata

Metadata

Assignees

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