Appendix D: Troubleshooting
The recurring traps from the book, in one place. When something misbehaves, start here.
A dangling or garbled cstring
Symptom: text draws as junk, or the game crashes when drawing dynamic text. Cause: a cstring from fmt.ctprintf (or a temp-allocator clone_to_cstring) was kept past the frame it was made in; the temp memory has since been recycled. Fix: format text at the moment you draw it, every frame. Never store a temp cstring in a struct. (Chapter 3.)
Silence, or a crash, from audio
Symptom: no sound, or a crash on the first PlaySound/LoadSoundFromWave. Cause: an audio call ran before rl.InitAudioDevice(). Fix: initialise the audio device first, and defer rl.CloseAudioDevice(). All loading and playing comes after. (Chapter 10.)
The wolf teleports through a wall after a hitch
Symptom: dragging the window, alt-tabbing, or a stall makes the wolf jump through obstacles. Cause: rl.GetFrameTime() returned a huge dt for one frame, and physics integrated a giant step. Fix: clamp it: dt := min(rl.GetFrameTime(), 0.05). (Chapter 3.)
A crash in the audio callback
Symptom: intermittent crash tied to sound. Cause: the AudioStream callback runs on raylib's audio thread with no Odin context, or it raced on game state. Fix: first line of the callback is context = runtime.default_context(); keep its shared state to a couple of plain values it only reads; don't allocate or call into gameplay from it. (Chapter 10.)
Mismatched types ... f32 vs f64
Symptom: a constant expression won't multiply with an f32. Cause: an untyped float constant assigned with := defaulted to f64 (e.g. air := 2 * JUMP_SPEED / GRAVITY). Fix: pin the type: air: f32 = 2 * JUMP_SPEED / GRAVITY. (Chapter 2 & 4.)
The HUD shakes with the world
Symptom: score/messages jitter during screen shake, or scroll off with the world. Cause: the HUD was drawn inside BeginMode2D/EndMode2D. Fix: draw the world inside Mode2D; draw the HUD and backdrop outside it, in screen space. (Chapters 5, 8, 9.)
The enumerated array won't compile
Symptom: an error about a missing key in [Enum]T{...}. Cause: an enumerated array literal must cover every enum value. Fix: add the missing row (this is the feature working; it's what keeps OBSTACLE_DEFS a true single source of truth). (Chapter 6.)
os.write_entire_file won't compile / result ignored
Symptom: "result must be handled" error, or read_entire_file overload not found. Cause: in this Odin version, os.write_entire_file returns an Error you must handle, and reads use os.read_entire_file_from_path(name, allocator) -> ([]byte, Error). Fix: _ = os.write_entire_file(...) to deliberately ignore; check err != nil on read. (Ch. 11.)
Non-reproducible runs
Symptom: the "fixed seed" course differs each run. Cause: something in the simulation read the wall clock or raylib's GetRandomValue instead of the seeded generator. Fix: all gameplay randomness goes through g.rng via context.random_generator; movement uses dt, never GetTime(). (Chapter 11.)
The game won't run on a headless machine
Symptom: it works on your desktop but fails on a server/CI box with no display. Cause: raylib needs a real window server, GPU/OpenGL context and audio device. Fix: run it on a desktop with a display. (This is expected: Chapter 12.)
odin run can't find raylib
Symptom: a link or import error on import rl "vendor:raylib". Cause: a broken or partial Odin install; vendor: couldn't be resolved. Fix: reinstall/verify Odin per the install guide; raylib ships inside it; you never fetch it separately. (Chapter 1.)