Yo Odin ยท Appendix D

Appendix D: Troubleshooting

The recurring traps, in one place. Most "it doesn't work" moments are one of these.

The page is blank / the wasm won't load

  • You opened it via file://. fetch/instantiateStreaming of the .wasm is blocked under file:// with a CORS error. Serve over HTTP: ./run.sh, then http://localhost:9000. (Chapters 3โ€“4.)
  • The .wasm isn't served as application/wasm. Streaming instantiation validates the MIME type and refuses anything else. Check your server's Content-Type for .wasm. Our Chapter 4 server sets it; some static hosts need configuring. (Chapter 4.)
  • A missing import. The browser console shows LinkError: ... function import requires ... when the JS import object is missing a function Odin expects (an odin_env hook, or an app_env entry you added in Odin but not in JS). Add it to the import object.

Crashes, garbage, or silent misbehaviour after a boundary crossing

  • You forgot context = runtime.default_context(). It must be the first line of every @(export) proc "c". Without it, anything touching the context (allocation, some core: calls) reads uninitialised state. Symptoms are weird and far from the cause. (Chapter 3.)

A string from JS turns to garbage

  • The scratch-buffer lifetime. batch.get(...) returns a string backed by one shared global buffer, valid only until the next call that touches scratch. Consume it (or copy it) before the next get. Same rule for str_concat/grey's buffers. (Chapters 6, 10.)

The renderer draws nothing, or draws nonsense

  • The opcode ends are out of sync. The Odin OP_* constants and the JS switch are one wire format in two files. If you added or changed an op on one side only, the JS interpreter reads an argument as an opcode and the buffer desyncs (often: default: return, so the frame just stops early). Edit both ends together. (Chapters 5, Appendix C.)
  • You didn't reset the buffer. Call batch.reset(&cmd) at the top of each frame, then record, then batch.flush. (Chapter 5.)

No sound

  • The AudioContext is suspended. Browsers keep it suspended until a user gesture. Call getCtx() (which resumes it) on the first keypress. (Chapter 11.)
  • The SND_* ids don't match. The Odin constants and the JS playSound switch are one list in two files. (Chapter 11.)

Build & tooling

  • odin check says "Undefined entry point procedure 'main'". You're checking WASM/library code without -no-entry-point. Use odin check src -target:js_wasm32 -no-entry-point. (Chapter 3.)
  • -out:serve says the output path is a directory. The binary name collides with the server/ package directory; that's why we name the package dir server/ and the binary serve. (Chapter 4.)
  • Port 9000 is already in use. Another instance (or another game) is bound to it. Stop it, or change PORT in server/serve.odin. Find it with lsof -i :9000 (macOS/Linux).
  • A core: call fails to compile on a newer Odin. The standard library evolves. The two most likely to drift here are os.read_entire_file_from_path (return shape) and core:net (Endpoint/listen_tcp signatures). Read the compiler's suggested overloads; it names the fix.