The Most Common Compilation Failures
Gleam's type system is designed to catch bugs at compile time instead of letting them explode in production. Gleam's type system catches bugs at compile time, which means more fighting with the compiler upfront. Here are the patterns that'll drive you nuts.
Pattern Matching Exhaustiveness Errors
You'll see something like:
error: Inexhaustive patterns
┌─ /src/app.gleam:15:3
│
15│ case user_result {
│ ^^^^ This case expression does not have a pattern for all possible values.
Missing patterns:
- Error(_)
You forgot to handle one of the possible values in your pattern match. Gleam forces you to handle every fucking case, which prevents runtime crashes but gets annoying as hell when you "know" certain cases won't happen. The exhaustiveness checking is stricter than your high school math teacher. Read more about pattern matching best practices and Result type handling in the official docs.
What actually works:
case user_result {
Ok(user) -> render_user_page(user)
Error(reason) -> {
// Handle the error case you were trying to ignore
io.println(\"User lookup failed: \" <> string.inspect(reason))
show_error_page()
}
}
Coming from JavaScript, this is annoying as hell. You can't just ignore errors and let everything explode at runtime. Gleam makes error handling explicit, which means more typing now but fewer angry phone calls at 3am when production goes down. BEAM's fault tolerance philosophy is baked into the language whether you like it or not. Check out the Gleam error handling guide and custom types documentation for more patterns. Stack Overflow Gleam questions often cover similar exhaustiveness checking issues.
Import Resolution Failures
You'll see something like:
error: Unknown module
┌─ /src/app.gleam:1:8
│
1 │ import gleam/http
│ ^^^^^^^^^^ The module `gleam/http` could not be found.
No module with this name was found in:
- /src
- The included libraries
The package isn't installed, the module name is wrong, or you're using an old package name that got refactored. Check the Gleam package index for current naming.
Try this first:
## First, check if you actually added the package
gleam deps list
## If it's missing, add it
gleam add gleam_http
## If it's there but still failing, check the correct module path
gleam docs build # This will show available modules
Wasted my Saturday on this. Turns out gleam/http
got renamed to gleam_http
in one of the v0.30 releases and I was using old Stack Overflow answers. The error message tells you nothing. As usual. Had to dig through the package docs, GitHub issues, and package migration guides to figure out what happened. Package naming conventions have evolved since early releases. The Gleam package search helps find current names, and Hex.pm lists all BEAM packages. Check migration guides for breaking changes between versions.
Type Mismatch Hell
You'll see something like:
error: Type mismatch
┌─ /src/api.gleam:23:10
│
23│ user.id
│ ^^^^^^^ Expected type `String`, found type `Int`.
Your mental model of the data doesn't match the actual type definition. This bites you constantly when working with external APIs or database records where you assume string IDs but get integers. Type inference can be both helpful and confusing as fuck. Read up on Gleam's type system and JSON decoding patterns for handling external data.
Stop assuming and convert it:
// Instead of assuming the type
let user_id_string = user.id // This fails if user.id is Int
// Convert explicitly (what you should have done from the start)
let user_id_string = int.to_string(user.id)
// Or handle both cases if you're feeling paranoid
let user_id_string = case user.id {
id if is_string(id) -> id
id if is_int(id) -> int.to_string(id)
_ -> \"unknown\" // This should never happen unless something's really fucked
}
Function Arity Errors That Confuse Everyone
You'll see something like:
error: Incorrect arity
┌─ /src/handlers.gleam:45:3
│
45│ handle_request(req)
│ ^^^^^^^^^^^^^^^^^^^ Expected 3 arguments, got 1
This function accepts these additional labelled arguments:
- context
- config
The function signature changed and you're calling it with the old parameters. Usually happens after you update dependencies and suddenly half your code is broken. Spent 2 hours debugging this when Wisp changed their handler signature in some recent version. Works fine in development, explodes in CI because of course it does.
The fix:
// Check the actual function definition
// Old way that's now broken:
handle_request(req)
// New way that works:
handle_request(req, context, config)
// Or if you don't have the required arguments:
handle_request(req, default_context(), default_config())
Dependency Resolution Nightmares
The Error You See (Pre-v1.12.0):
error: Dependency resolution failed
An error occurred while determining what dependency packages and
versions should be downloaded.
The error from the version resolver library was:
Unable to find compatible versions for the version constraints in your
gleam.toml. The conflicting packages are:
- app
- wisp
- mist
- gleam_otp
- gleam_json
Your dependencies want different versions of the same underlying package, and the resolver can't find a combination that makes everyone happy. I've seen this exact error break CI for 3 different teams. Spent hours trying to figure out which package was causing the conflict.
Good news: Gleam v1.12.0 (released August 5, 2025) finally fixed this with better error messages. Instead of the cryptic stuff above, you get:
error: Dependency resolution failed
There's no compatible version of `gleam_otp`:
- You require wisp >= 1.0.0 and < 2.0.0
- wisp requires mist >= 1.2.0 and < 5.0.0
- mist requires gleam_otp >= 0.9.0 and < 1.0.0
- You require lustre >= 5.2.1 and < 6.0.0
- lustre requires gleam_otp >= 1.0.0 and < 2.0.0
Now you can actually see the conflict: mist
wants gleam_otp < 1.0.0
but lustre
wants gleam_otp >= 1.0.0
.
Update your dependencies to compatible versions:
[dependencies]
wisp = \">= 1.1.0 and < 2.0.0\" # Use newer wisp that works with gleam_otp 1.x
lustre = \">= 5.2.1 and < 6.0.0\"
Custom Type Definition Errors
You'll see something like:
error: Unknown type
┌─ /src/models.gleam:12:15
│
12│ pub fn process_user(user: User) -> String {
│ ^^^^ The type `User` is not defined.
You forgot to import the type, it's defined in a different module, or you have a typo in the type name. Spent 30 minutes once thinking the compiler was broken until I realized I spelled User
as Uesr
. Delete node_modules and try again won't work here - this is just a typo.
Fix:
// If User is defined in another module:
import app/models.{type User}
// Or import the whole module:
import app/models
pub fn process_user(user: models.User) -> String
// If you're defining it in the same file, make sure the type definition comes first:
pub type User {
User(id: Int, name: String, email: String)
}
pub fn process_user(user: User) -> String {
user.name <> \" (\" <> user.email <> \")\"
}
Record Update Syntax Confusion
You'll see something like:
error: Syntax error
┌─ /src/user_logic.gleam:34:21
│
34│ User(..user, name: new_name, email: new_email)
│ ^^^^ I was expecting `}` here.
The record update syntax has specific rules about field order and you're breaking them.
Fix:
// Wrong - you can't mix spread with individual field updates like this
User(..user, name: new_name, email: new_email)
// Right - use spread at the end
User(name: new_name, email: new_email, ..user)
// Or update specific fields only
User(..user, name: new_name)
Why This Trips People Up: The spread syntax (..user
) must come at the end, unlike JavaScript where object spread can go anywhere. This is a deliberate design choice in Gleam but feels wrong as hell if you're coming from JS. Took me forever to unlearn this muscle memory.
External Function FFI Errors
You'll see something like:
error: Unknown external target
┌─ /src/crypto.gleam:5:1
│
5 │ @external(erlang, \"crypto\", \"hash\")
│ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ External implementations must target \"erlang\" or \"javascript\".
You're trying to call external Erlang or JavaScript functions but the syntax is wrong or the target module doesn't exist.
Fix:
// Make sure the external function exists in the target runtime
@external(erlang, \"crypto\", \"hash\")
@external(javascript, \"./crypto.mjs\", \"hash\")
fn hash(algorithm: String, data: String) -> String
// And create the JavaScript implementation file crypto.mjs:
export function hash(algorithm, data) {
const crypto = require('crypto');
return crypto.createHash(algorithm).update(data).digest('hex');
}
Watch out: External functions are where type safety goes to die. Spent an entire afternoon debugging why our "type-safe" Gleam code kept crashing with undefined
errors that took down prod for 2 hours. Turns out the JavaScript FFI function was fine for normal inputs but completely broke when someone passed an empty string. Type system can't see inside external code, so you're flying blind. FFI is where type safety goes to die.
This covers most compilation errors you'll hit. Gleam's errors are usually precise about what's wrong, you just need to understand the type system's rules. Once you get used to the compiler's nagging, you'll actually miss it in other languages.