
Zig Debugging
- 322 installs
- 155 repo stars
- Updated June 27, 2026
- mohitmishra786/low-level-dev-skills
Diagnose Zig crashes, undefined behavior, and failing tests using debugger workflows, stack traces, and sanitizer habits before you tag a systems release.
About
Zig-debugging skill teaches Claude low-level diagnosis for Zig programs: interpreting panics, using debuggers, isolating allocator bugs, and shrinking failing tests. It targets CLI tools, native services, and performance-sensitive codebases where ship-quality means proving fixes under real binaries, not just passing superficial compile checks.
- lldb/gdb-style Zig debugging steps
- Stack trace and panic interpretation
- Memory and allocator bug patterns
- Test isolation and minimal repros
- Release vs debug build tradeoffs
Zig Debugging by the numbers
- 322 all-time installs (skills.sh)
- +23 installs in the week ending Aug 4, 2026 (Skillselion tracking)
- Ranked #126 of 596 Debugging skills by installs in the Skillselion catalog
- Data as of Aug 4, 2026 (Skillselion catalog sync)
npx skills add https://github.com/mohitmishra786/low-level-dev-skills --skill zig-debuggingAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 322 |
|---|---|
| repo stars | ★ 155 |
| Last updated | June 27, 2026 |
| Repository | mohitmishra786/low-level-dev-skills ↗ |
What it does
Diagnose Zig crashes, undefined behavior, and failing tests using debugger workflows, stack traces, and sanitizer habits before you tag a systems release.
Files
Zig Debugging
Purpose
Guide agents through debugging Zig programs: GDB/LLDB sessions, interpreting Zig panics and error return traces, std.debug.print logging, debug build configuration, and IDE integration.
Triggers
- "How do I debug a Zig program with GDB?"
- "How do I interpret a Zig panic message?"
- "How do I use std.debug.print for debugging?"
- "Zig is showing an error return trace — what does it mean?"
- "How do I set up Zig debugging in VS Code?"
- "How do I get a stack trace from a Zig crash?"
Workflow
1. Build for debugging
# Debug build (default) — full debug info, safety checks
zig build-exe src/main.zig -O Debug
# With build system
zig build # uses Debug by default
zig build -Doptimize=Debug
# Run directly with debug output
zig run src/main.zig2. GDB with Zig
Zig emits standard DWARF debug information compatible with GDB:
# Build with debug info
zig build-exe src/main.zig -O Debug -femit-bin=myapp
# Launch GDB
gdb ./myapp
# GDB session
(gdb) break main
(gdb) run arg1 arg2
(gdb) next # step over
(gdb) step # step into
(gdb) continue
(gdb) print my_var
(gdb) info locals
(gdb) bt # backtraceBreak on Zig panics:
(gdb) break __zig_panic_start
(gdb) break std.builtin.default_panic3. LLDB with Zig
lldb ./myapp
(lldb) b main
(lldb) r arg1 arg2
(lldb) n # next
(lldb) s # step into
(lldb) p my_var # print
(lldb) frame variable
(lldb) bt # backtrace
(lldb) c # continue
# Break on panic
(lldb) b __zig_panic4. Interpreting Zig panics
Zig panics include the source location and reason:
thread 'main' panic: index out of bounds: index 5, len 3
/home/user/src/main.zig:15:14
/home/user/src/main.zig:42:9
???:?:?: (name not available)Common panic messages:
| Panic | Cause |
|---|---|
index out of bounds: index N, len M | Slice/array OOB access |
integer overflow | Arithmetic overflow in Debug/ReleaseSafe |
attempt to unwrap null | Optional access .? on null |
reached unreachable code | unreachable executed |
casting... | Invalid enum tag or union access |
integer cast truncated bits | @intCast with value out of range |
out of memory | Allocator failed |
5. Error return traces
Zig tracks where errors propagate with error return traces:
error: FileNotFound
/home/user/src/main.zig:30:20: 0x10a3b in openConfig (main)
const f = try std.fs.openFileAbsolute(path, .{});
^
/home/user/src/main.zig:15:25: 0x10b12 in run (main)
const cfg = try openConfig("/etc/myapp.conf");
^
/home/user/src/main.zig:8:20: 0x10c44 in main (main)
try run();
^The trace shows the exact try chain where the error propagated. Read bottom-up: main → run → openConfig.
Enable in release builds:
// build.zig
const exe = b.addExecutable(.{
.name = "myapp",
.root_source_file = b.path("src/main.zig"),
.target = target,
.optimize = optimize,
.error_tracing = true, // enable even in ReleaseFast
});6. std.debug.print for tracing
const std = @import("std");
pub fn main() !void {
const x: u32 = 42;
const name = "world";
// Basic print (always to stderr)
std.debug.print("x = {d}, name = {s}\n", .{ x, name });
// Print any value (useful for structs)
const point = Point{ .x = 1, .y = 2 };
std.debug.print("point = {any}\n", .{point});
// Formatted output
std.debug.print("hex: {x}, binary: {b}\n", .{ x, x });
// Log levels (respects compile-time log level)
const log = std.log.scoped(.my_module);
log.debug("debug info: {d}", .{x});
log.info("started processing", .{});
log.warn("unusual condition", .{});
log.err("failed: {s}", .{"reason"});
}7. std.log configuration
// Override default log level at root
pub const std_options = std.Options{
.log_level = .debug, // .debug | .info | .warn | .err
};
// Custom log handler
pub fn logFn(
comptime level: std.log.Level,
comptime scope: @TypeOf(.enum_literal),
comptime format: []const u8,
args: anytype,
) void {
const prefix = "[" ++ @tagName(level) ++ "] (" ++ @tagName(scope) ++ "): ";
std.debug.print(prefix ++ format ++ "\n", args);
}
pub const std_options = std.Options{
.logFn = logFn,
};8. VS Code / IDE integration
Install the zig.vscode-zig extension and CodeLLDB.
.vscode/launch.json:
{
"version": "0.2.0",
"configurations": [
{
"type": "lldb",
"request": "launch",
"name": "Debug Zig",
"program": "${workspaceFolder}/zig-out/bin/myapp",
"args": [],
"cwd": "${workspaceFolder}",
"preLaunchTask": "zig build"
}
]
}.vscode/tasks.json:
{
"version": "2.0.0",
"tasks": [
{
"label": "zig build",
"type": "shell",
"command": "zig build",
"group": { "kind": "build", "isDefault": true },
"problemMatcher": ["$zig"]
}
]
}Related skills
- Use
skills/zig/zig-compilerfor build modes and debug info flags - Use
skills/debuggers/gdbfor GDB fundamentals - Use
skills/debuggers/lldbfor LLDB fundamentals - Use
skills/zig/zig-cinteropwhen debugging mixed Zig/C code
Zig Debugging Patterns Reference
Panic Handler Customization
// Custom panic handler in root file (src/main.zig)
pub fn panic(msg: []const u8, error_return_trace: ?*std.builtin.StackTrace, ret_addr: ?usize) noreturn {
_ = ret_addr;
std.debug.print("\n=== PANIC ===\n{s}\n", .{msg});
if (error_return_trace) |trace| {
std.debug.dumpStackTrace(trace.*);
}
std.debug.dumpCurrentStackTrace(null);
std.process.exit(1);
}Stack Trace Utilities
// Print current stack trace
std.debug.dumpCurrentStackTrace(null);
// Capture stack trace
var addresses: [32]usize = undefined;
var trace = std.builtin.StackTrace{
.instruction_addresses = &addresses,
.index = 0,
};
std.debug.captureStackTrace(null, &trace);
// Later:
std.debug.dumpStackTrace(trace);
// Print source location at comptime
const location = @src();
std.debug.print("{s}:{d}\n", .{ location.file, location.line });Format Specifiers for std.debug.print
// {}: default format
// {d}: decimal integer
// {x}: hex (lowercase)
// {X}: hex (uppercase)
// {o}: octal
// {b}: binary
// {e}: float scientific
// {f}: float fixed
// {s}: string/slice
// {c}: character (u8 as char)
// {u}: unicode codepoint
// {any}: any type (uses std format)
// {*}: pointer address
const val: u32 = 255;
std.debug.print("{d} {x} {b} {o}\n", .{val, val, val, val});
// → 255 ff 11111111 377
// Struct formatting
const Point = struct { x: f32, y: f32 };
const p = Point{ .x = 1.5, .y = 2.7 };
std.debug.print("{}\n", .{p});
// → main.Point{ .x = 1.5e+00, .y = 2.7e+00 }
// Pointer
const ptr: *const u32 = &val;
std.debug.print("{*}\n", .{ptr});
// → *const u32@0x7fff...Compile-Time Debugging
// Print at compile time with @compileLog
fn fibonacci(comptime n: u32) u32 {
@compileLog("computing fibonacci for n =", n);
if (n <= 1) return n;
return fibonacci(n - 1) + fibonacci(n - 2);
}
// Inspect type at compile time
const T = u32;
@compileLog(@typeName(T)); // → "u32"
@compileLog(@typeInfo(T)); // → TypeInfo union
@compileLog(@sizeOf(T)); // → 4
@compileLog(@alignOf(T)); // → 4
@compileLog(@bitSizeOf(T)); // → 32GDB Commands for Zig Types
# Print Zig slice
(gdb) p my_slice
# Shows: {ptr = 0x..., len = 5}
# Inspect slice elements
(gdb) p *my_slice.ptr@my_slice.len
# Print Zig optional
(gdb) p my_optional
# Print error union
(gdb) p my_error_union
# Zig stack frames show Zig-mangled names
# Demangle with:
(gdb) set print demangle on
(gdb) set demangle-style autoMemory Debugging
// Use GeneralPurposeAllocator for leak detection
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer {
const leaked = gpa.deinit();
if (leaked == .leak) std.debug.print("Memory leaked!\n", .{});
}
const allocator = gpa.allocator();
// Debug allocation failures
var gpa = std.heap.GeneralPurposeAllocator(.{
.safety = true, // Check for use-after-free
.never_unmap = true, // Keep memory mapped (detect UAF faster)
.retain_metadata = true, // Keep freed block metadata
}){};Testing and Debug Assertions
const testing = std.testing;
// In tests
try testing.expect(x == 42);
try testing.expectEqual(@as(u32, 42), x);
try testing.expectEqualStrings("hello", result);
try testing.expectError(error.NotFound, fallible_fn());
// Debug assertions (only in Debug/ReleaseSafe)
std.debug.assert(x > 0); // panics if false
// Custom assertion with message
if (x <= 0) {
std.debug.panic("Expected positive x, got {d}", .{x});
}Zig-Specific GDB Breakpoints
# Break on any Zig panic
(gdb) break __zig_panic_start
# Set via pattern matching for specific panic source
(gdb) rbreak zig.*panic
# Watch for slice OOB
(gdb) watch -l array_var.len
# Catch allocation failures in GPA
(gdb) break std.heap.GeneralPurposeAllocator.alloc