Errors and Error Handling

16.1 Error Sets and Error Unions

An error is a value, like an enum across the program. anyerror is the global set of all errors.

Errors can't carry payloads. Use a "diagnostics" out-parameter for details (16.5).

const FileError = error{
    NotFound,
    PermissionDenied,
};
const ParseError = error{ InvalidChar, Overflow };
const AppError = FileError || ParseError;           // merge error sets

fn parseDigit(c: u8) ParseError!u8 {                // error union: ParseError or u8
    if (c < '0' or c > '9') return error.InvalidChar;
    return c - '0';
}

fn loadConfig(path: []const u8) !Config {           // inferred error set
    _ = path;
    return error.NotFound;
}

16.2 Handling Errors

// try: return the error to the caller (≡ catch |e| return e)
const d = try parseDigit('7');

// catch: default value
const e = parseDigit('x') catch 0;

// catch with a capture
const f = parseDigit('x') catch |err| blk: {
    std.debug.print("failed: {s}\n", .{@errorName(err)});
    break :blk 0;
};

// catch unreachable: assert it can't fail
const g = parseDigit('5') catch unreachable;

// switch on errors
parseDigit('x') catch |err| switch (err) {
    error.InvalidChar => std.debug.print("bad char\n", .{}),
    error.Overflow => {},
};

// if/else with error capture
if (parseDigit('3')) |val| {
    _ = val;
} else |err| {
    _ = err;
}
_ = .{ d, e, f, g };

16.3 Error Return Traces

In Debug and ReleaseSafe, an error returned from main prints an error return trace showing every try point it passed through. It's extremely helpful for debugging.

@errorReturnTrace() gives programmatic access.

16.4 main and Errors

pub fn main() !void {
    try run();             // an error here prints its name + trace and exits with code 1
}

16.5 Patterns for Rich Error Information

The standard library uses this pattern (e.g., std.json.Diagnostics, std.zon parse diagnostics).

const Diagnostics = struct {
    line: usize = 0,
    message: []const u8 = "",
};

fn parse(src: []const u8, diag: *Diagnostics) !Ast {
    if (src.len == 0) {
        diag.* = .{ .line = 1, .message = "empty input" };
        return error.ParseFailed;
    }
    // ...
}

16.6 Panics

@panic("unrecoverable state");                     // custom panic
std.debug.assert(x > 0);                           // panics if false in Debug/ReleaseSafe (UB hint in fast modes)
pub const panic = std.debug.FullPanic(myPanicFn);  // override the panic handler in the root file (0.14+)