You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
IO.get_env on Node truncates names at embedded NUL #1449
IO.get_env on the emitted Node lane treats an embedded NUL as the end of an environment-variable name. The native C and Bun lanes reject that name with Fail(ENOENT) instead. This reads a different variable than the caller named.
Reproducer
On main 8578b7c, with HOME set (as required by the existing tests/io/get_env.bend fixture):
import Base
def report(tag: String, r: Result<&1, &1, (U32 & String), String>) -> IO(Unit):
match r:
case Done{value}:
IO.print(String.append(tag, "=set"))
case Fail{error}:
(code, message) = error
IO.print(String.append(tag, String.append("=error:", U32.show(code))))
def main() -> IO(U32):
do IO<U32>:
exact : Result<&1, &1, (U32 & String), String> <- IO.get_env("HOME")
a : Unit <- report("exact", exact)
nul : Result<&1, &1, (U32 & String), String> <- IO.get_env("HOME\0suffix")
b : Unit <- report("nul", nul)
return 0
Emit a standalone .js and run it with Bun and Node; emit .c, compile with clang and run with --threads 1 --gpu off.
Observed actual Bend output:
Lane
Exact HOME
HOME\0suffix
C
exact=set
nul=error:2
Bun
exact=set
nul=error:2
Node
exact=set
nul=set
Cause / expected behavior
get_env.c already checks io_nul(name, n). get_env.js relies on Object.hasOwn(process.env, name), but Node's environment proxy truncates at NUL even for its own-property check. A direct host control with an owned sentinel key confirms Object.hasOwn(process.env, key + "\0suffix") === true on Node and false on Bun.
Reject NUL-containing names before asking the host environment, preserving the existing ENOENT result and exact-name / own-property semantics. No change to valid environment values, host FFI support, or error-code policy is needed.
Duplicate search for get_env found no matching open issue or PR (the closed #809 UTF-8 decoding report is a different boundary).
Fixed in #1451. The updated permanent fixture fails before on Node and passes afterward on C, Bun and Node; valid/missing environment lookups remain unchanged. Repo gate 54/54 and rendered documentation checked. Native checker/gates/caps are untouched.
Update: #1450 appeared during verification with the same production guard. Closing the duplicate #1451 so #1450 remains the merge candidate; the stricter ENOENT regression, documentation, and actual before/after C/Bun/Node evidence remain linked in #1451.
Activity
oxura commentedon Oct 9, 2026
Fixed in #1451. The updated permanent fixture fails before on Node and passes afterward on C, Bun and Node; valid/missing environment lookups remain unchanged. Repo gate 54/54 and rendered documentation checked. Native checker/gates/caps are untouched.
Update: #1450 appeared during verification with the same production guard. Closing the duplicate #1451 so #1450 remains the merge candidate; the stricter ENOENT regression, documentation, and actual before/after C/Bun/Node evidence remain linked in #1451.