One of Zig’s strengths is compiler diagnostics. Zig tries to explain problems precisely instead of silently accepting dangerous behavior.
One of Zig’s strengths is compiler diagnostics. Zig tries to explain problems precisely instead of silently accepting dangerous behavior.
At first, Zig error messages can feel strict. That is normal. The compiler is teaching you how Zig thinks about types, memory, errors, and control flow.
This appendix explains common errors beginners see, why they happen, and how to fix them.
C.1 “unused local constant”
Example:
pub fn main() void {
const x = 10;
}Error:
error: unused local constantZig does not allow unused variables by default.
Why?
Unused variables often mean:
- forgotten code
- incomplete logic
- debugging leftovers
- bugs
Fix it by using the variable:
std.debug.print("{}\n", .{x});Or discard it explicitly:
_ = x;The _ means “I intentionally ignore this value.”
C.2 “unused function parameter”
Example:
fn add(a: i32, b: i32) i32 {
return a;
}Error:
error: unused function parameterYou declared b but never used it.
Fix:
fn add(a: i32, _: i32) i32 {
return a;
}Using _ says the parameter is intentionally unused.
C.3 “expected type”
Example:
const x: i32 = "hello";Error:
error: expected type 'i32'You tried to store a string in an integer variable.
Fix the type:
const x: []const u8 = "hello";Or fix the value:
const x: i32 = 123;C.4 “expected type ‘usize’”
Example:
const nums = [_]i32{ 1, 2, 3 };
const x = nums[-1];Array indexes use usize, which is an unsigned integer type.
Negative indexes are invalid.
Fix:
const x = nums[0];C.5 “integer overflow”
Example:
const x: u8 = 255;
const y = x + 1;In safe build modes, Zig checks integer overflow.
A u8 can only hold values from 0 to 255.
Fix it by using a larger type:
const x: u16 = 255;
const y = x + 1;Or use wrapping operators if overflow is intentional:
const y = x +% 1;+% means wrapping addition.
C.6 “division by zero”
Example:
const x = 10 / 0;Error:
error: division by zeroZig checks this at compile time when possible.
Fix:
const x = 10 / 2;Or validate runtime input before division.
C.7 “index out of bounds”
Example:
const nums = [_]i32{ 10, 20, 30 };
const x = nums[10];The array has only 3 items.
Valid indexes are:
| Index | Value |
|---|---|
0 | 10 |
1 | 20 |
2 | 30 |
Fix:
const x = nums[2];C.8 “unable to resolve comptime value”
Example:
var x: i32 = 10;
const y = comptime x;comptime values must be known during compilation.
A runtime variable is not known at compile time.
Fix:
const x: i32 = 10;
const y = comptime x;Or remove comptime.
C.9 “value with comptime-only type”
Example:
const T = i32;
var x = T;Types exist only at compile time.
You cannot store a type as a normal runtime value.
Usually you meant:
var x: T = 10;C.10 “error is ignored”
Example:
fn fail() !void {
return error.BadThing;
}
pub fn main() void {
fail();
}Error:
error: error is ignoredZig requires explicit error handling.
Fix with try:
try fail();Or catch:
fail() catch {};C.11 “expected optional type”
Example:
const x: i32 = null;null only works with optional types.
Fix:
const x: ?i32 = null;Optional types use ?.
C.12 “attempt to use null value”
Example:
var maybe: ?i32 = null;
const x = maybe.?;.? unwraps an optional.
But maybe is null.
Fix:
if (maybe) |value| {
std.debug.print("{}\n", .{value});
}Or assign a real value first.
C.13 “expected error union type”
Example:
const x: i32 = 10;
try x;try only works with error unions like:
!i32Fix:
const x = try loadValue();Where loadValue returns an error union.
C.14 “cannot dereference non-pointer type”
Example:
var x: i32 = 10;
x.* = 20;.* only works on pointers.
Fix:
var x: i32 = 10;
var p = &x;
p.* = 20;C.15 “cannot assign to constant”
Example:
const x = 10;
x = 20;const values cannot change.
Fix:
var x = 10;
x = 20;C.16 “missing semicolon”
Example:
const x = 10
const y = 20;Zig statements usually end with ;.
Fix:
const x = 10;
const y = 20;C.17 “expected statement, found ‘else’”
Example:
if (true)
std.debug.print("yes\n", .{});
else
std.debug.print("no\n", .{});This can happen when formatting or structure is incorrect.
Usually braces fix it clearly:
if (true) {
std.debug.print("yes\n", .{});
} else {
std.debug.print("no\n", .{});
}C.18 “switch must handle all possibilities”
Example:
const dir = enum {
north,
south,
};
switch (dir) {
.north => {},
}You forgot .south.
Fix:
switch (dir) {
.north => {},
.south => {},
}Or add else.
C.19 “unreachable code”
Example:
return;
std.debug.print("hello\n", .{});Code after return cannot run.
Fix by removing unreachable code.
C.20 “unable to evaluate constant expression”
Example:
var x: i32 = 10;
const y = x + 1;This may happen when Zig expects compile-time evaluation but sees runtime state.
Common causes:
- runtime variables in compile-time contexts
- runtime loops in type construction
- runtime allocation during compile-time execution
Fix by separating compile-time and runtime logic.
C.21 “no field named”
Example:
const Point = struct {
x: i32,
y: i32,
};
const p = Point{
.x = 1,
.z = 2,
};Point has no field named z.
Fix:
const p = Point{
.x = 1,
.y = 2,
};C.22 “no member named”
Example:
std.math.fakeFunction();The function does not exist.
Possible causes:
- typo
- old documentation
- version mismatch
- removed API
Fix by checking:
- Zig version
- official documentation
- autocomplete/LSP suggestions
C.23 “dependency loop detected”
Example:
const A = B;
const B = A;The compiler cannot determine which declaration comes first.
Real dependency loops are usually more complex and involve:
- structs
- comptime logic
- imports
- default field values
Fix by restructuring dependencies.
C.24 “local variable is never mutated”
Example:
var x = 10;But x never changes.
Zig suggests using const.
Fix:
const x = 10;This helps make programs clearer and safer.
C.25 “expected ‘,’ after argument”
Example:
add(1 2);Arguments are separated with commas.
Fix:
add(1, 2);C.26 “root struct has no member named ‘main’”
Example:
fn start() void {}Zig executable programs need:
pub fn main() void {}Fix by defining main.
C.27 “C import failed”
Example:
const c = @cImport({
@cInclude("missing.h");
});Possible causes:
- header not installed
- include path missing
- library not installed
Fix by installing the dependency or adding include paths.
C.28 “unable to find zig installation directory”
This usually means the Zig executable was moved incorrectly or installed incompletely.
Fixes:
- reinstall Zig
- use the official release archive
- avoid moving internal Zig files separately
C.29 “Segmentation fault at address”
This is a runtime crash, not a compile error.
Common causes:
| Cause | Example |
|---|---|
| Null pointer | Dereferencing null |
| Invalid pointer | Using freed memory |
| Out-of-bounds access | Bad indexing |
| Uninitialized memory | Reading undefined |
Debug build modes help catch these problems earlier.
C.30 Read Errors Carefully
A beginner mistake is reading only the first line of the error.
Zig errors often contain:
- the main error
- notes
- referenced declarations
- source locations
- type information
- dependency chains
Example structure:
error: expected type 'i32'
note: found type '[]const u8'
note: called from hereThe notes are important.
C.31 A Good Debugging Process
When Zig reports an error:
- Read the full message.
- Find the exact line.
- Identify the types involved.
- Ask: compile-time or runtime?
- Ask: pointer, optional, or error union?
- Reduce the code to a tiny example.
- Fix one error at a time.
Do not panic if the compiler prints many errors. One incorrect type can cause a chain of secondary errors.
C.32 The Compiler Is Teaching You
Zig’s compiler is strict because Zig wants programs to be explicit.
Many beginner frustrations come from hidden assumptions:
- “I thought this could be null.”
- “I thought this would convert automatically.”
- “I thought this could fail silently.”
- “I thought this was mutable.”
- “I thought this happened at runtime.”
Zig tries to remove those hidden assumptions from the language.
That strictness is one reason Zig programs become easier to reason about as they grow.