Troubleshooting#
This page helps you when a mod does not load, does nothing, or breaks a save. It explains the error messages and how to test your changes.
When the game validates#
The game reads and checks every file in public/config/ when the page starts. If one file is invalid, loading stops. You see the screen "The passage is closed." with the error message and a retry button. Nothing is fixed for you.
A message looks like this:
Invalid key « synergies.3.special.announce » in equipment.json.
The key is a dotted path to the first faulty entry. Counting starts at 0, so synergies.3 is the fourth synergy. A cross-file mistake reads "Invalid cross reference in <file>: key « <key> »".
Each entry has one owner. A field in the wrong file is refused, even when the right file has a valid copy. Repeating a JSON key is also refused, including escaped spellings of the same key. A missing recipe output or book model names the physical file and path to fix. Errors use file order, then entry order; the network response order does not choose the error.
The most common errors#
Find the end of the key in your message, then read across.
| Key ends with | Cause | Fix |
|---|---|---|
….hook | Unknown hook, or an event written where a hook belongs. | Copy the name from the catalogue. |
….on | Unknown event, or a hook written where an event belongs. | Use an event from the catalogue. |
….op | Not one of add, mul, min, max, chance. | Fix the spelling. |
….value | Not a finite number; for chance, not between 0 and 1; or "$value" used where nothing rolls a value (synergies, sets, onHit). | Write a plain number. |
….then | Missing on a chance modifier, or present on another operation. | Give chance a then, and only chance. |
….action | Unknown action. | Pick an action from the catalogue. |
….params.<name> | Unknown parameter, wrong type, or a marker ($skill…) the source refuses. | Check the action's parameters in the catalogue. |
….params.<name> (absent) | A required parameter is missing. | Add it. |
….when.<name> | Unknown condition, or a value that is not text, number or true/false. | Use a condition from the catalogue. |
….id | A listener id that is not lowercase with dashes, or used twice in the list. | Use crit-heal style ids, unique in the list. |
….cooldown | Neither "floor" nor a whole number from 0 to 1000. | Fix the value. |
affixes | A duplicate id, a family that is not an effect, malformed tiers, weight of 0 or less, minLevel outside 1 to 100. The key has no index. | Check each affix you added. |
engrave | An effect without a reagent, a reagent for an unknown effect, or a reagent that does not exist. | Give each effect exactly one real reagent. |
enchanting.permanentEffects.<reagent> | A side names a mechanic that does not exist, or a mechanic listens to the wrong event. | Declare the mechanic; weapons use hit.dealt. |
Magic and terrain errors name their own path, such as magic.syllables.<id>.definition. The path says which entry is wrong.
What the validator does not check#
- Text keys. Shared filter, object and family labels are checked at load. Some other text fields accept a key without checking its translation. Add it to all language files in
public/config/locales/, with the same variables. - Meaning. A negative duration or a cell off the map passes. The validator checks names, types and listed values.
- Context. A
whenthat reads a key the hook never fills is accepted and never matches. Look at the hook's context in the catalogue. - Unused enchantments. A mechanic that no reagent uses is accepted.
Testing a mod locally#
- Run
npm run devand open the address it prints, usuallyhttp://localhost:5173/. - Edit a file in
public/config/, then reload the page. Files are read at start-up. - Open the "Debug" tab of the side window, then "Equipment". Create a test item there and equip it.
- Read "Active modifiers and listeners of the player, by source". It lists every active modifier and listener, with its cooldown. It draws no random numbers.
Tool exports. The forge lets you copy its JSON envelope, tagged below-the-iron/forge-catalogs@1. The loot editor downloads loot-catalogs.json, tagged below-the-iron/loot-catalogs@1. These are editing envelopes, not mod packages. Open them with the matching tool and the same explicit companion files.
The forge edits equipment and crafting, plus recipe items, catalysts, family tiers, recipe availability and recipe prices. The loot editor edits distributions and book models. Other sections stay read-only. It warns if exported neighbours differ, and keeps the neighbours from the open project.
An import does not refresh your editing session. If another tool changed any of the fifteen files, reload before saving. A failed save keeps your work exportable. A partial write lists the files already written; a lost response means the result is unconfirmed. Neither case sends your edit again automatically.
Saves#
A save stores the exact state of a run. The game accepts only version 20 saves.
A save older than version 20 is refused: there is no converter. The game shows a translated message and offers a new game. A newer, unknown version is refused the same way. In both cases the stored file is left intact.
A version 20 save that breaks the current rules is refused at validation. For example, a creature in the save may use a rule you removed from creature-rules.json. The game never fills the gap with defaults and never resets anything silently.
- Add definitions instead of renaming or removing them.
- Keep identifiers stable.
- Start a new game after changing data that existing saves point to.