Replace bun_core::Error intern machinery with per-crate thiserror enums

Merged
RO
4mo ago
+8058-4927

Replaces the runtime-interned global error set bun_core::Error (#[repr(transparent)] NonZeroU16) and the err!() macro with idiomatic per-crate thiserror enums.

Why

err!(Name) cached a global RwLock<Vec<&'static str>> intern lookup in a per-site AtomicU16. Not a const, so it could never be a pattern; every dispatch was an if-else guard chain with no exhaustiveness check (worst case ~40 arms in FetchTasklet::to_body_value_error). This was Zig's anyerror, ported.

Design

One #[derive(thiserror::Error)] enum per crate at <crate_dir>/error.rs, re-exported as crate::Error + crate::Result<T, E = Error>:

#[derive(Debug, Clone, Copy, PartialEq, Eq, thiserror::Error)]
pub enum Error {
    #[error("TooManyRedirects")]
    TooManyRedirects,
    #[error(transparent)] Sys(#[from] bun_errno::SystemErrno),
    #[error(transparent)] Alloc(#[from] bun_alloc::AllocError),
}
  • err!("OutOfMemory") (92 sites, 17 crates) routes to bun_alloc::AllocError
  • errno/fs names (ENOENT, FileNotFound, AccessDenied, NoSpaceLeft, NameTooLong, NotDir, EISDIR, BadPathName, all E*) route to bun_errno::SystemErrno (2 bytes). NOT bun_sys::Error (40 bytes; the context tier).
  • Cross-crate duplicate names are intentional: css::Error::InvalidCharacter != sql::Error::InvalidCharacter.
  • #[error(transparent)] X(#[from] ...) for pass-through; ? composes via From.
  • Every enum also impls bun_core::output::ErrName so Output::err(e, ...) keeps working.
  • Every #[error("...")] string preserves the original err!("...") literal verbatim.
  • No new deps (thiserror = "2" was already a workspace dep). No dyn Error / Box<dyn Error> / anyhow.

Deleted

  • macro_rules! err + .bun_err link-section (src/bun_core/lib.rs)
  • SEED, EXTRA, intern_slow, intern_cached, Error::intern, Error::from_errno, Error::from_raw, Error::name, Error::as_u16, the RwLock<Vec<&'static str>> intern table (src/bun_core/result.rs)
  • named_error_set! macro (all 31 call sites deleted; enums now derive thiserror::Error directly)
  • OrWriteFailed trait
  • ErrorCode::from(bun_core::Error) / ErrorCode::to_error() (src/jsc/ErrorCode.rs): these aliased the intern u16 onto the JS error-code table index. Callers of Errorable::err() now pass ErrorCode(ErrorCode::JS_ERROR_OBJECT) directly.
  • impl From<bun_sys::Error> for bun_core::Error (src/sys/lib.rs); replaced with From<bun_sys::Error> for bun_errno::SystemErrno.

bun_core::Error still exists but is now bun_core's own local 13-variant thiserror enum (for the bun_core::io::Write trait's return type).

Foundation

  • bun_errno::SystemErrno now impls Display + core::error::Error (via strum::IntoStaticStr).
  • bun_alloc::AllocError now impls Display + core::error::Error.

Scope

  • 1,641 err!() call sites across 40 crates, 449 distinct names
  • 523 files changed, +7435 -4331

Verification

  • cargo check --workspace --keep-going: green
  • cargo build --workspace --keep-going: green (post-mono const asserts)
  • bun run rust:check-all: green across all 10 targets (linux/macos/windows x x64/aarch64 + android)

Deferred

  • bun_runtime (192 local variants) and bun_install (117) kept as flat per-crate enums rather than module-level split. Splitting would change == semantics for the 9 names that span modules inside bun_runtime; both enums are still <=4 bytes with Sys nesting.
  • Shrinking bun_sys::Error (40 bytes), variant payloads, $ERR_* JS machinery: out of scope.