Lua Scripting: Troubleshooting
This page covers common problems you may encounter when writing or debugging FreeMinecraftModels prop scripts, plus tips for working with the lazy config generation system. If you are looking for working examples, see Examples & Patterns. If you are just getting started, see Getting Started.
Common Issues
1. Config file not loading / Script not attached
Symptom: The prop spawns but does not respond to clicks or any hooks.
Causes and fixes:
-
No
.ymlconfig file exists yet. FMM generates the config lazily on first prop spawn. The first time a model is spawned, FMM creates the.ymlconfig asynchronously with default values (enabled, no scripts). You must edit the generated config to add your script filenames and then respawn the prop. -
The config has
isEnabled: false. Open the.ymlfile next to the model file and setisEnabled: true. -
The
scripts:list is empty. Add your script filenames:isEnabled: true
scripts:
- my_script.lua -
The
.ymlfilename does not match the model filename. The config must have the same base name as the model file. For example,torch_01.fmmodelneedstorch_01.ymlin the same directory.
2. Script file not found
Symptom: Console shows: [FMM Scripts] Script 'my_script.lua' not found in scripts/ folder
Causes and fixes:
-
Wrong directory. Script files must be in
plugins/FreeMinecraftModels/scripts/, not next to the model file. -
Filename mismatch. The name in the
.ymlconfig must exactly match the filename in thescripts/folder, including case and the.luaextension. -
File not present. Double-check that the
.luafile actually exists in thescripts/folder.
3. Script does not load at all
Symptom: No console error, but no hooks fire.
Check the server console for Lua syntax errors during startup. The most common causes are:
- Missing
endto close a function orifblock - Unmatched parentheses
- Missing comma between hook definitions in the returned table
- File does not end with
.lua
If there is a Lua error, the console will print a [Lua] error block with the filename, line number, and a description.
4. Hook never fires
Symptom: The script loads (you see the [FMM Scripts] Bound script message), but a specific hook never triggers.
Verify the hook name is spelled exactly as listed in the hook reference. Common mistakes:
| Wrong name | Correct name |
|---|---|
on_click | on_right_click or on_left_click |
on_interact | on_right_click |
on_hit | on_left_click |
on_tick | on_game_tick |
on_remove | on_destroy |
on_arrow_hit | on_projectile_hit |
Also verify that the prop actually has a backing armor stand entity. Some prop configurations may not produce a clickable entity.
5. Timeout / execution budget exceeded
Symptom: Console shows a message like:
[Lua] my_script.lua took 73ms in 'on_game_tick' (limit: 50ms) -- script disabled to prevent lag.
The script was doing too much work in a single hook call. Common causes:
- Iterating over too many entities in
on_game_tick - Creating too many particles per tick
- Running expensive loops without spreading work across ticks
Fix: Move heavy work behind a state-based cooldown or use scheduler:run_repeating() with a reasonable interval instead of on_game_tick.
6. context.event is nil in a click hook
This should not normally happen for on_left_click and on_right_click, but always guard with:
if context.event then
context.event.cancel()
end
Inside scheduler callbacks, context.event is always nil -- this is expected behavior. Event modification can only happen inside the event hook itself.
7. Animation does not play
Symptom: play_animation() returns false, or nothing visible happens.
Causes and fixes:
-
Wrong animation name. The name must exactly match what is defined in the model file. Check your
.bbmodelor.fmmodelfor the correct animation name. -
Model has no animations. Not all models have animations. Verify the model file actually contains animation data.
-
Players are not in resource pack range. The animation is server-side, but players need the FMM resource pack loaded to see the model at all.
8. Particles do not appear
- Verify the particle name is a valid Bukkit
Particleenum value in UPPER_CASE:"FLAME", not"flame". - Verify the location is in a loaded chunk. If no players are nearby, the chunk may be unloaded.
- Verify
countis at least 1. - Some particles (like
DUST) need specific extra data that the basespawn_particle()may not support. Use standard particles likeFLAME,HEART,HAPPY_VILLAGER,NOTE,ENCHANT, etc.
9. Sound does not play
- Verify the sound name is a valid Bukkit
Soundenum constant in UPPER_CASE. Example:"BLOCK_NOTE_BLOCK_HARP", not"block.note_block.harp". - Verify the location coordinates are correct (not all zeros or NaN).
- Verify volume is greater than 0.
10. State resets unexpectedly
context.state is per-prop-instance and persists for the prop's lifetime. If state seems to reset:
- The prop may have been removed and respawned (each spawn creates a new instance).
- You may be reading state in a scheduler callback using the wrong context variable. Always use the callback's own context parameter.
How Lazy Config Generation Works
Understanding the lazy config system helps avoid confusion when setting up new props:
-
First spawn: When a prop spawns and no sibling
.ymlfile exists, FMM creates the config asynchronously. This means the file is written in a background thread and is not immediately available. -
Default values: The generated config has
isEnabled: trueand an emptyscripts: []list. -
No scripts on first spawn: Because the config is created after the prop has already spawned (and has no scripts listed), the prop will have no scripts attached on its first spawn.
-
Edit and respawn: After FMM creates the config, you edit it to add your script filenames. The next time the prop spawns, scripts will be loaded.
-
Config location: The
.ymlfile is created in the same directory as the model file, with the same base name. For example:- Model:
plugins/FreeMinecraftModels/models/fountain.fmmodel - Config:
plugins/FreeMinecraftModels/models/fountain.yml
- Model:
- Place model in
models/ - Place script in
scripts/ - Spawn the prop once (generates the
.yml) - Edit the
.ymlto addscripts: [my_script.lua] - Respawn the prop -- script is now active
Reading Error Messages
When something goes wrong in a Lua prop script, the console prints a [Lua] error block. These messages tell you exactly which file, which line, which hook, and what went wrong in plain English.
A typical error looks like this:
[Lua] Error in 'my_door.lua' at line 12 during 'on_right_click':
[Lua] -> You tried to call a method or function that doesn't exist.
[Lua] -> Check the method name for typos, or make sure you're using ':' (colon) for method calls, not '.' (dot).
[Lua] -> Script has been disabled for this entity to prevent further errors.
The system translates common Lua errors into plain English:
| Raw Lua error | What the console tells you |
|---|---|
attempt to call nil | You tried to call a method or function that doesn't exist. Check for typos and : vs . usage. |
index expected, got nil | You tried to access a field on something that is nil. Check that earlier code initialized it. |
attempt to index | You tried to access a property on a nil or invalid value. |
bad argument | A function received the wrong type of argument. The message shows the expected vs. actual type. |
| Timeout | Your script took Xms in 'hook_name' (limit: 50ms) -- script disabled to prevent lag. |
Always read the full [Lua] error message before diving into the code. It usually points you straight to the fix.
Do Not Assume Undocumented Methods Exist
The FMM Lua API exposes a specific set of methods. Do not assume shorthand or alternative names exist. Common mistakes:
context.prop:get_location()-- does not exist. Usecontext.prop.current_location(a field, not a method).context.prop:set_animation("open")-- does not exist. Usecontext.prop:play_animation("open", true, true).context.event:setCancelled(true)-- does not exist. Usecontext.event.cancel().context.cooldowns:check_local(...)-- does not exist in FMM. Usecontext.statewithscheduler:run_later()for cooldowns.context.player-- does not exist in FMM prop scripts. Usecontext.world:get_nearby_players()to find players near the prop.context.boss-- does not exist. That is EliteMobs. Usecontext.propin FMM.
When in doubt, check the Prop API page. If it is not documented there, it does not exist.
Debug Tips
1. Use context.log:info() liberally
Add log messages at every step when debugging:
on_right_click = function(context)
context.log:info("Right click received!")
context.log:info("Event present: " .. tostring(context.event ~= nil))
context.log:info("State is_open: " .. tostring(context.state.is_open))
end
2. Check the console on startup
FMM logs [FMM Scripts] Bound script 'X' to prop 'Y' for each successful binding. If you do not see this message, the config or script was not loaded.
3. Verify the model file path
The .yml config must be a sibling of the model file (same directory, same base name). Verify this by checking:
- Model file path:
plugins/FreeMinecraftModels/models/my_model.fmmodel - Config file path:
plugins/FreeMinecraftModels/models/my_model.yml
4. Test hooks individually
When building a complex script, start by testing each hook in isolation. Add one hook at a time and verify it fires before moving to the next.
5. Check for typos in hook names
The most common reason a hook does not fire is a misspelled hook name. The script will load without errors, but the misspelled hook function will be rejected during validation.
Beginner Progression Path
If you want to learn this system from scratch, this progression works well:
- Write a file with only
api_version = 1andon_spawnthat logs a message. - Add the script to a prop config and verify the log message appears.
- Add
on_right_clickand log when the prop is clicked. - Add
on_left_clickwithcontext.event.cancel()for invulnerability. - Play an animation on right-click.
- Add a sound on right-click.
- Add state and toggle behavior.
- Add a zone watch for proximity detection.
- Only then move into complex multi-hook scripts with schedulers.
Each step builds on the previous one, and you can test at every stage.
Next Steps
- Getting Started -- file structure, hooks, first script walkthrough, copy-paste templates
- Prop API -- full API reference
- Examples & Patterns -- complete working scripts you can study and adapt