oven-sh/bun · error · Error

${loc}: HOST_EXPORT(${e.symbol}, rust) param `${p.raw}` is a

Error message

${loc}: HOST_EXPORT(${e.symbol}, rust) param `${p.raw}` is a reference; use a raw pointer

What it means

For hooks declared `HOST_EXPORT(symbol, rust)`, the generator forwards the signature verbatim into an `#[unsafe(no_mangle)] pub extern "Rust" fn` item. Reference parameters are rejected because an extern "Rust" fn item requires explicit lifetimes for borrowed types, which the generator does not synthesize. The fix is always the same: take a raw pointer (`*const T` / `*mut T`) at the ABI boundary and dereference inside.

Source

Thrown at src/codegen/generate-host-exports.ts:422

      // `SYSV_ABI` lazyPropCb-style getters). `&JSGlobalObject` is
      // ABI-identical to non-null `*const JSGlobalObject`; C++ never passes
      // null here.
      const body = retIsJsResult
        ? `    host_fn::host_fn_lazy(g, ${impl})`
        : `    host_fn::host_fn_lazy_passthrough(g, ${impl})`;
      return `
// ${loc}
${emitNoMangle(e.abi, e.symbol, "g: &JSGlobalObject", "JSValue", body)}`;
    }
    case "rust": {
      // `extern "Rust"` link-time hook: forward the safe signature verbatim
      // (no pointer rewriting; `extern "Rust"` ABI == native Rust ABI).
      // Reference params/returns are rejected — `extern "Rust" fn` items need
      // explicit lifetimes for borrowed types and the generator does not
      // synthesise them. Use raw pointers in the impl signature instead.
      for (const p of e.params)
        if (p.ty.startsWith("&"))
          throw new Error(
            `${loc}: HOST_EXPORT(${e.symbol}, rust) param \`${p.raw}\` is a reference; use a raw pointer`,
          );
      if (e.ret.startsWith("&"))
        throw new Error(
          `${loc}: HOST_EXPORT(${e.symbol}, rust) return type \`${e.ret}\` is a reference; use a raw pointer`,
        );
      const sig = e.params.map(p => `${p.name}: ${p.ty}`).join(", ");
      const call = e.params.map(p => p.name).join(", ");
      return `
// ${loc}
#[allow(dead_code, unreachable_pub, unused)]
#[unsafe(no_mangle)]
pub extern "Rust" fn ${e.symbol}(${sig}) -> ${e.ret} {
    ${impl}(${call})
}`;
    }
    case "generic": {
      const sig = e.params.map(p => `${p.name}: ${p.cTy}`).join(", ");

View on GitHub (pinned to 8c5296ac45)

Solutions

  1. Change each reference parameter to a raw pointer: `&T` → `*const T`, `&mut T` → `*mut T`
  2. Reborrow inside the body: `let r = unsafe { &*ptr };` (or use the established Bun helpers for JSGlobalObject pointers)
  3. If the hook must stay reference-typed, use the `jsc` or `c` variant, which go through a generated thunk that rewrites pointers

Example fix

// before
// HOST_EXPORT(BunOnHook, rust)
pub fn on_hook(global: &JSGlobalObject, cb: &JSValue) { ... }

// after
// HOST_EXPORT(BunOnHook, rust)
pub fn on_hook(global: *const JSGlobalObject, cb: *const JSValue) { ... }
Defensive patterns

Strategy: validation

Validate before calling

// reject reference params on HOST_EXPORT(..., rust) hooks
import { Glob } from "bun";
for (const f of new Glob("src/**/*.rs").scanSync(".")) {
  const text = await Bun.file(f).text();
  const re = /\/\/\s*HOST_EXPORT\(\s*(\w+)\s*,\s*rust\s*\)[^;]*?pub\s+fn\s+\1\s*\(([^)]*)\)/gs;
  for (const m of text.matchAll(re)) {
    if (m[2].split(",").some(p => p.trim().replace(/^[\w\s]+:\s*/, "").startsWith("&"))) {
      throw new Error(`${f}: rust hook ${m[1]} takes a reference — use a raw pointer`);
    }
  }
}

Type guard

function isRawPointerTy(ty: string): boolean {
  return !ty.trim().startsWith("&");
}

Prevention

When it happens

Trigger: Writing `// HOST_EXPORT(MyHook, rust)` above a `pub fn` whose parameters include `&JSGlobalObject`, `&str`, `&T`, or `&mut T`.

Common situations: Adding new link-time Rust hooks (the `rust` linkage variant, as opposed to `jsc`/`c`) and copying the safe signature from a regular function; the jsc/c variants tolerate references because thunks are generated, the rust variant does not.

Related errors


AI-assisted analysis of oven-sh/bun@8c5296ac45 (2026-08-16). Data as JSON: /api/errors/a8b1cd34a48b8874. Report an issue: GitHub.