gleam-lang/gleam · error
`panic` expression evaluated.
Error message
`panic` expression evaluated.
What it means
Gleam's `panic` keyword is compiled to JavaScript as a `throw` of a `GleamError` built by the generated `throwMakeError(...)` call (compiler-core/src/javascript/expression.rs:2584-2634). When the `panic` expression has no custom label (`panic as "..."`), the default message is exactly "`panic` expression evaluated.". The thrown error also carries the originating .gleam module, line number, and function name, so it pinpoints which panic site was reached. This is the designed runtime abort for Gleam programs compiled to JavaScript, not a compiler bug.
Source
Thrown at compiler-core/src/javascript/expression.rs:2584
let message = match message {
Some(m) => self.not_in_tail_position(None, |this| this.wrap_expression(arena, m)),
None => string(
arena,
"`todo` expression evaluated. This code has not yet been implemented.",
),
};
self.throw_error(arena, "todo", &message, *location, vec![])
}
fn panic(
&mut self,
arena: &'doc DocumentArena<'a, 'doc>,
location: &'a SrcSpan,
message: Option<&'a TypedExpr>,
) -> Document<'a, 'doc> {
let message = match message {
Some(m) => self.not_in_tail_position(None, |this| this.wrap_expression(arena, m)),
None => string(arena, "`panic` expression evaluated."),
};
self.throw_error(arena, "panic", &message, *location, vec![])
}
pub(crate) fn throw_error<Fields>(
&mut self,
arena: &'doc DocumentArena<'a, 'doc>,
error_name: &'a str,
message: &Document<'a, 'doc>,
location: SrcSpan,
fields: Fields,
) -> Document<'a, 'doc>
where
Fields: IntoIterator<Item = (&'a str, Document<'a, 'doc>)>,
{
self.tracker.make_error_used = true;
let module = self.module_name.clone().to_doc(arena).surround(
arena,View on GitHub (pinned to 7e623aa83d)
Solutions
- Read the thrown error's module/line/function fields (e.g. module `my_module`, line 42, function `head`) and open that .gleam file — that is the exact panic site
- Replace the panicking branch with real handling: return `Result(t, e)` or handle the case instead of aborting
- If the abort is intentional, add a label `panic as "descriptive message"` so the next failure names the cause
- If the panic is inside a dependency, check the call's preconditions (non-empty list, present key, valid range) and validate before calling
Example fix
// before (src/my_module.gleam)
pub fn head(list: List(a)) -> a {
case list {
[x, ..] -> x
[] -> panic
}
}
// after
pub fn head(list: List(a)) -> Result(a, Nil) {
case list {
[x, ..] -> Ok(x)
[] -> Error(Nil)
}
} Defensive patterns
Strategy: try-catch
Validate before calling
// Gleam: give callers a checkable value instead of an abort
pub fn head(list: List(a)) -> Result(a, Nil) {
case list {
[x, ..] -> Ok(x)
[] -> Error(Nil)
}
} Try / catch
// JavaScript host around generated Gleam .mjs
import * as myModule from "./build/dev/javascript/my_pkg/my_module.mjs";
import { GleamError } from "./build/dev/javascript/my_pkg/gleam.mjs";
try {
myModule.doThing();
} catch (e) {
if (e instanceof GleamError) {
// payload identifies the panic site (module, line, function)
console.error("Gleam panic reached:", e);
process.exit(1);
}
throw e;
} Prevention
- Prefer Result-returning helpers over panicking ones for any input-dependent path
- Never leave a bare `panic` in scaffolding that ships; at minimum label it `panic as "reason"`
- Treat any panic reached in production output as a defect to fix at the panic site, not an exception to catch routinely
When it happens
Trigger: Executing generated JavaScript (via `gleam run` on the JavaScript target, or importing the emitted .mjs from Node) in a way that reaches a message-less `panic`: a bare `panic` in a function body, a `case` branch ending in `panic`, or a dependency helper that panics on the input you passed (e.g. an 'impossible' empty-input branch).
Common situations: Scaffolding code where `panic` was left as a placeholder; a case branch believed unreachable that is in fact reachable at runtime; unexpected inputs such as an empty list or a missing key reaching a panicking helper inside a published Hex package.
Related errors
- JavaScript generator could not identify imported module name
- Custom type must have at least one definition here
- BEAM compiler instance exited: {status}
- Unable to start Tokio async runtime
- Unable to start Tokio async runtime
AI-assisted analysis of gleam-lang/gleam@7e623aa83d (2026-08-17).
Data as JSON: /api/errors/b046cfda31bb8b5f.
Report an issue: GitHub.