Skip to main content

afterburner_core/
error.rs

1// SPDX-License-Identifier: BUSL-1.1
2// Copyright (c) 2026 vertexclique
3// Licensed under the Business Source License 1.1.
4// Change Date: 4 years after this version's release. Change License: Apache-2.0.
5
6//! Errors produced by any `Combustor` implementation or the `BurnCache`.
7
8use thiserror::Error;
9
10/// Every failure mode Afterburner exposes to callers. Keep the set closed:
11/// callers match on it exhaustively.
12#[derive(Debug, Error)]
13pub enum AfterburnerError {
14    /// JS source failed to compile (syntax error, unsupported construct, etc.).
15    #[error("compile failed: {0}")]
16    CompileFailed(String),
17
18    /// `thrust` was invoked with a `ScriptId` the engine doesn't know about.
19    /// Usually means the script was `extinguish`ed or never `ignite`d.
20    #[error("script not found (hash mismatch or extinguished)")]
21    ScriptNotFound,
22
23    /// The script consumed all fuel allotted by `FuelGauge::fuel`.
24    #[error("fuel exhausted")]
25    FuelExhausted,
26
27    /// The script tried to allocate past `FuelGauge::memory_bytes`.
28    #[error("memory limit exceeded")]
29    MemoryLimit,
30
31    /// Wall-clock `FuelGauge::timeout_ms` elapsed before the script finished.
32    #[error("execution timed out")]
33    Timeout,
34
35    /// The WASM runtime trapped for any reason not caught above (division by
36    /// zero, unreachable, integer overflow, etc.).
37    #[error("wasm trap: {0}")]
38    WasmTrap(String),
39
40    /// JSON could not be produced or consumed at the host boundary.
41    #[error("serialization error: {0}")]
42    Serialize(#[from] serde_json::Error),
43
44    /// The script's result exceeded the per-call output ceiling
45    /// (`FuelGauge::output_bytes`, default 64 MiB). Applies uniformly
46    /// to the JSON result capture, raw result bytes, and script-mode
47    /// stdout. Surfaces as a typed error rather than an opaque
48    /// guest-side I/O trap or a JSON parse failure on truncated bytes.
49    #[error("script output exceeded {limit} byte ceiling (raise FuelGauge::output_bytes)")]
50    OutputTooLarge { limit: usize },
51
52    /// The module returned raw bytes (`Uint8Array` / `ArrayBuffer`)
53    /// through a `Value`-shaped API (`run` / `run_raw`). Use the
54    /// `OutputValue`-returning variants (`run_out` / `run_raw_out`)
55    /// to receive raw results.
56    #[error(
57        "script returned {len} raw bytes; use run_out / run_raw_out (OutputValue) to receive them"
58    )]
59    UnexpectedRawOutput { len: usize },
60
61    /// A host function returned an error to the script.
62    #[error("host error: {0}")]
63    Host(String),
64
65    /// The script requested a capability the active `Manifold` does not
66    /// grant (e.g. `fs.readFileSync` with `FsAccess::None`, or an FS
67    /// path outside the allowed roots). The inner string names the
68    /// denied operation - useful for audit logs and error messages.
69    #[error("permission denied: {0}")]
70    PermissionDenied(String),
71
72    /// The admission layer rejected this thrust because the associated
73    /// tenant's token bucket is empty. Retry after `retry_after_ms`.
74    ///
75    /// `tenant` is the raw `u32` from `TenantId` (or `None` for the
76    /// unrestricted path). Callers can wrap back into `TenantId` if
77    /// they need the newtype.
78    #[error("rate limited (tenant={tenant:?}, retry after {retry_after_ms}ms)")]
79    RateLimited {
80        tenant: Option<u32>,
81        retry_after_ms: u64,
82    },
83
84    /// The thrust engine refused the job because its global in-flight
85    /// cap is reached (pooling-allocator slot exhaustion). This is a
86    /// backpressure signal: slow down or provision more workers.
87    #[error("engine overloaded (in-flight cap reached)")]
88    Overloaded,
89
90    /// The script called `process.exit(n)` inside daemon mode. The CLI
91    /// propagates the exit code to `std::process::exit`; library callers
92    /// can inspect the code and decide what to do.
93    #[error("process.exit({0})")]
94    ProcessExit(i32),
95
96    /// Generic engine-internal failure that doesn't fit a specific variant.
97    /// Use sparingly - prefer adding a typed variant.
98    #[error("engine error: {0}")]
99    Engine(String),
100}
101
102/// Convenience alias used across the workspace.
103pub type Result<T> = core::result::Result<T, AfterburnerError>;