-
-
Notifications
You must be signed in to change notification settings - Fork 3
Troubleshooting
The loader never fails silently. Every rejection produces one specific message, shown in the mod menu under the mod's name and printed to the log. This page lists all of them.
If you are debugging, run the validator first — it produces the same diagnostics as the game, on your terminal, without launching anything:
odmod-check mymod.odmodAdd --revoke UI (or any module) to rehearse a user turning a capability off,
and --no-run to check the archive without instantiating.
| Message | Cause | Fix |
|---|---|---|
cannot open <path> |
The file is missing or unreadable. | Check the path and permissions. |
<path> is empty |
Zero bytes. | Your build script produced nothing; check it for a silent failure. |
<path> is not a readable ZIP archive |
Not a ZIP, or truncated. |
.odmod is a ZIP. Repack. A partial download does this too. |
corrupt central directory at entry N |
The ZIP index is damaged. | Repack from source. |
MANIFEST.json failed to decompress (corrupt or bad CRC) |
The entry's CRC does not match. | Repack. If it recurs, your packer is writing bad CRCs. |
mod.wasm failed to decompress (corrupt or bad CRC) |
As above. | As above. |
These are checked against the ZIP index before anything is decompressed, so a bomb costs nothing. See modding.md for the full table.
| Message | Cause | Fix |
|---|---|---|
archive has N entries, over the limit of 4096 |
Too many files. | Bundle assets — one archive per directory of small files, or fewer, larger files. |
archive expands to more than 256 MiB |
Total decompressed size too large. | Ship less, or ship assets outside the mod. |
entry "X" expands N:1, over the 200:1 per-entry limit |
One file compresses suspiciously well. | Almost always a zip bomb; if it is genuinely a huge run of zeros, store it compressed differently or generate it at runtime. |
archive expands N:1 overall, over the 100:1 limit |
The whole archive compresses too well. | Same. Note this can trip even when every individual entry passes. |
entry "X" nests deeper than 16 directories |
Path too deep. | Flatten your data/ layout. |
Rejected outright, because these are how archives escape their extraction directory. The message always names the entry and the reason.
| Reason fragment | Meaning |
|---|---|
absolute path |
Entry name starts with /. |
drive-letter path |
Entry name looks like C:/…. |
'..' path component |
Traversal. |
'.' path component |
Redundant, and a normalisation trick. |
backslash in path |
Windows separator. Use /. |
control character in name |
Bytes below 0x20, or 0x7F. |
name is not valid UTF-8 |
Includes overlong encodings — two spellings of one character is how a traversal slips past a byte comparison. |
Most packers will not produce these. If you hit one, whatever generated the archive is doing something unusual — check it rather than working around it.
| Message | Cause | Fix |
|---|---|---|
archive has no MANIFEST.json |
Missing. | Every mod needs one, at the archive root. |
MANIFEST.json must be the first entry in the archive…found "X" first |
Wrong order. | Add the manifest first. tools/pack_odmod.sh does this; with the zip CLI, name it first. Directory entries before it are fine. |
MANIFEST.json is N bytes, over the 262144 byte limit |
Enormous manifest. | Something is wrong; the manifest is metadata. |
archive has no mod.wasm |
Missing module. | The module must be named exactly mod.wasm at the archive root. |
mod.wasm is N MiB, over the 64 MiB limit |
Module too large. | If you are shipping a language runtime (Tier 2), strip and optimise it. |
mod.wasm is not a WebAssembly binary (bad magic…) |
Not wasm. | You packed the wrong file — a native .o, a .wat, or an empty file. |
All of these are prefixed MANIFEST.json:.
| Message | Fix |
|---|---|
is not a JSON object |
Invalid JSON, or the top level is an array. Trailing commas are the usual culprit. |
missing integer field "schema" |
Add "schema": 1. |
unsupported schema N |
This build understands schema 1. |
missing field "id" / "name" / "version" / "gearbox"
|
Add it. |
"id" must be lowercase reverse-DNS using [a-z0-9._-] and contain a dot |
Lowercase only. The id is your Storage namespace and trust-pinning key; on a case-insensitive filesystem com.you.Mod and com.you.mod would collide as two identities sharing one store. |
"version" must be semver MAJOR.MINOR.PATCH |
1.0 is not enough; write 1.0.0. |
"name" is longer than 96 bytes |
Shorten it. |
"gearbox" must be MAJOR.MINOR |
Write "1.1", not "1" or "1.1.0". |
targets Gearbox vX.Y, this build provides v1.1 — different major versions are not compatible |
A major mismatch is refused outright; there is no partial compatibility across majors. Retarget the mod. |
(warning, not an error) targets Gearbox v1.2 but this build provides v1.1; newer APIs will be missing
|
You declared a newer minor than the host has. The mod still loads. If it actually imports something from that minor, instantiation fails and names the symbol — see §7. Declaring the oldest minor you actually need is the safer habit. |
missing array field "modules" |
Even a mod using only Core should list ["Core"]. |
"modules" must contain only strings |
No nested objects. |
dependency is missing "id" / dependency id "X" is not a valid mod id
|
Dependencies are parsed and validated but not yet resolved. |
"publicKey" must be prefixed "ed25519:" |
Or omit it. Signatures are not verified yet. |
Requests an unknown module produces its own message listing every module the host knows:
MANIFEST.json requests unknown module "Filesystem". Known modules: Core, GameState.Read, …
This is a hard error by design. Silently dropping a capability you believe you have is worse than refusing to load.
Wrong API major version:
targets Gearbox v2.0, this build provides v1.0 — different major versions are not compatible
A newer minor is not an error — it loads with a warning, and anything from the newer minor is simply absent.
Limits are clamped, not rejected. Asking for more memory or fuel than the
host allows produces a warning (requested N memory pages, clamped to 1024),
not a failure. Ceilings: 1024 pages (64 MiB) and 100,000,000 fuel/turn.
Now the archive is fine and the module is being instantiated.
| Message | Cause | Fix |
|---|---|---|
mod.wasm did not load: <engine message> |
Malformed or uses a wasm feature this build does not enable. | SIMD, threads, exceptions and GC proposals are off. Bulk memory and reference types are on. Build for plain wasm32-freestanding. |
mod.wasm imports "X"."Y", which this host does not provide. |
You imported something outside the ABI. | Almost always your toolchain adding env.abort, wasi_snapshot_preview1.*, or similar. Use freestanding / no-std flags. See §7. |
mod.wasm imports "X"."Y" but the <Module> module is not granted |
You declared the capability but the user revoked it in Advanced, or you never declared it. | Declare it in modules, and handle being refused — a user is allowed to say no. |
mod.wasm imports a non-function ("X"."Y") |
You tried to import a memory, table or global. | Only host functions may be imported. Define your own memory. |
mod.wasm did not instantiate: <engine message> |
Usually a start function trapping, or a limits conflict. | Avoid a wasm start section; do your work in mod_load. |
could not create an execution environment for the mod |
Host resource failure. | Rare; report it. |
duplicate mod id, already provided by <file> |
Two .odmod files in the mods directory declare the same id. |
Delete one. The id is the identity, not the filename. |
This is the single most likely failure for a language other than C, and it is always the same shape: your toolchain emitted an import you did not ask for.
| Import you see | Toolchain | Fix |
|---|---|---|
env.abort |
AssemblyScript | Compile with --use abort=. |
env.trace, env.seed
|
AssemblyScript | Avoid trace() and Math.random(). |
wasi_snapshot_preview1.* |
TinyGo, Rust with wasm32-wasi, Zig with wasi
|
Target wasm-unknown (TinyGo), wasm32-unknown-unknown (Rust), wasm32-freestanding (Zig). |
env.memory |
Anything linking with --import-memory
|
Drop that flag; define your own memory. |
env.__linear_memory, env.__stack_pointer
|
Object file, not linked | You packed a .o. Run the linker. |
go.* |
Standard Go (GOOS=js) |
Standard Go cannot target this host; use TinyGo. |
Inspect what your module actually imports before guessing:
python3 tools/wasm_imports.py mymod.odmodIt accepts a .wasm or a .odmod, flags any import outside the ABI, checks
that mod_load is exported, and exits non-zero if either is wrong — so it drops
straight into a build script.
These disable the mod mid-session and show the message in the mod menu.
| Message | Cause | Fix |
|---|---|---|
mod_load refused the load with code N |
Your mod_load returned non-zero. |
That is your own code declining. Return 0 to accept. |
mod does not export mod_load |
Export missing or misnamed. | The name must be exactly mod_load. Check your export attribute actually took effect — inspect the export section. |
mod_pre_turn: Exception: out of bounds memory access |
You handed the host, or yourself, a bad pointer. | Bounds-check your own indices; the host refuses out-of-range (ptr,len) but cannot fix your arithmetic. |
…: Exception: unreachable |
A trap: failed assertion, panic, integer divide by zero, or a language runtime aborting. | In Rust/Zig/AS, a panic compiles to unreachable. Handle the error instead. |
…: Exception: wasm operand stack overflow |
Runaway recursion. | The stack is 64 KiB. |
| Mod stops mid-turn with no further logging | Fuel exhausted. | You exceeded limits.fuelPerTurn. Raise it in the manifest (capped at 100,000,000) or do less work per turn. Not enforced on web — see below. |
WASM module load failed: fast interpreter offset overflow |
The module is too big for WAMR's fast interpreter. | A function exceeds the INT16_MAX operand-stack or local-count limit. Only CPython does this in practice. Build the host with -DOD_MODS_FAST_INTERP=OFF; see Languages. |
the mod called exit(71) |
wasi-libc gave up during startup. | 71 is EX_OSERR. wasi-libc calls _Exit(EX_OSERR) when its preopen scan gets anything but EBADF from fd_prestat_get. If you see this from your own runtime, it died before your code ran. |
mod_load: Exception: instruction limit exceeded |
Load ran out of fuel. |
mod_load is metered against limits.loadFuel, not fuelPerTurn. It defaults to 500,000,000, so hitting this means either you set loadFuel yourself or your interpreter really is that expensive to start. |
aborted: <your message> |
You called gearbox_abort. |
Working as intended. |
Worth knowing so you do not chase them.
-
Cannot override max memory with value greater than module max memoryin the log. This comes from the engine and is misleading: your manifest'smemoryPagesis the cap that applies. Verified bytests/mod_runtime_test.cpp, which grows a module to exactly the manifest limit and no further. -
Needs reload to take effectis a state, not a failure. You enabled a mod while a game was running, or changed its permissions. Reload the modloader. -
ignoring unrecognised entry "X"is a warning. OnlyMANIFEST.json,mod.wasm,thumbnail.png,signature.binanddata/**are used; anything else is carried but ignored. - A thumbnail warning never fails a load. A non-PNG or one over 512×512 is dropped with a note.
-
archive contains N data/ file(s) but does not request the Assets module— your files are there but unreadable by you. Add"Assets"tomodules.
| Symptom | Explanation |
|---|---|
Panels do nothing, panel_register returns 0 |
is_headless is 1. Self-play training runs thousands of turns with no renderer. Check gearbox_env and skip UI work. |
| An infinite loop hangs the browser tab | Fuel is not enforced on web. The browser exposes no instruction limit. On desktop the interpreter terminates you. Bound your own loops. |
| Mods vanish after a page refresh on web | Deliberate — web mods are session-scoped. Re-add them; loading is hot, so it costs one interaction. |
| The mod menu says the runtime is unavailable | The game was built with -DOD_ENABLE_MODS=OFF, or WAMR could not be fetched at configure time. The game still runs; mods cannot. |
| You cannot turn on AI Learning | A mod is enabled. The two are mutually exclusive: a mod can change what the AI observes, and training against that corrupts data/ai/model.bin invisibly. Disable your mods first. |