Found while comparing outbound wasi:http behavior against wasmtime-wasi-http 47.0.3 (src/p3/). Two header-error variants are declared but never produced (wasi/src/http.ts:108-109 vs the throw sites at 395-479, which only ever raise invalid-syntax and immutable).
1. forbidden is never produced — and wire behavior is platform-dependent
wasmtime rejects 9 headers at fields.append/set with header-error.forbidden (lib.rs:142-151: connection, keep-alive, proxy-authenticate, proxy-authorization, proxy-connection, transfer-encoding, upgrade, host, http2-settings). We accept everything and let fetch's forbidden list own the outcome (recorded divergence, wasi/src/http.ts:58-60).
Two guest-observable consequences beyond the recorded "platform owns them":
fields.append("host", ...) succeeds here and errors on wasmtime — guests probing for forbidden headers see opposite results.
- What actually reaches the wire for quasi-forbidden headers differs across our own platforms: browsers, undici (Node), and Deno enforce different forbidden lists, so the same guest sends different requests depending on which JS runtime hosts it. We are not even self-consistent, let alone wasmtime-consistent.
Enforcing wasmtime's list at the fields level would restore both kinds of consistency, at the cost of rejecting headers some platforms would happily transmit. Policy call.
2. size-exceeded is never produced
wasmtime enforces field_size_limit (128 KiB default, ctx.rs:9) → size-exceeded. We have no limit; a guest can build unboundedly large fields, and any platform rejection surfaces later as internal-error from headers.append at send time (wasi/src/http.ts:932-942) rather than as the typed header-error at mutation time.
Low stakes, but if issue 1 lands a fields-level enforcement pass, a size limit belongs in the same place.
Found while comparing outbound wasi:http behavior against wasmtime-wasi-http 47.0.3 (
src/p3/). Twoheader-errorvariants are declared but never produced (wasi/src/http.ts:108-109 vs the throw sites at 395-479, which only ever raiseinvalid-syntaxandimmutable).1.
forbiddenis never produced — and wire behavior is platform-dependentwasmtime rejects 9 headers at
fields.append/setwithheader-error.forbidden(lib.rs:142-151: connection, keep-alive, proxy-authenticate, proxy-authorization, proxy-connection, transfer-encoding, upgrade, host, http2-settings). We accept everything and let fetch's forbidden list own the outcome (recorded divergence, wasi/src/http.ts:58-60).Two guest-observable consequences beyond the recorded "platform owns them":
fields.append("host", ...)succeeds here and errors on wasmtime — guests probing for forbidden headers see opposite results.Enforcing wasmtime's list at the
fieldslevel would restore both kinds of consistency, at the cost of rejecting headers some platforms would happily transmit. Policy call.2.
size-exceededis never producedwasmtime enforces
field_size_limit(128 KiB default,ctx.rs:9) →size-exceeded. We have no limit; a guest can build unboundedly large fields, and any platform rejection surfaces later asinternal-errorfromheaders.appendat send time (wasi/src/http.ts:932-942) rather than as the typedheader-errorat mutation time.Low stakes, but if issue 1 lands a fields-level enforcement pass, a size limit belongs in the same place.