Skip to main content

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 .yml config file exists yet. FMM generates the config lazily on first prop spawn. The first time a model is spawned, FMM creates the .yml config 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 .yml file next to the model file and set isEnabled: true.

  • The scripts: list is empty. Add your script filenames:

    isEnabled: true
    scripts:
    - my_script.lua
  • The .yml filename does not match the model filename. The config must have the same base name as the model file. For example, torch_01.fmmodel needs torch_01.yml in 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 .yml config must exactly match the filename in the scripts/ folder, including case and the .lua extension.

  • File not present. Double-check that the .lua file actually exists in the scripts/ 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 end to close a function or if block
  • 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 nameCorrect name
on_clickon_right_click or on_left_click
on_interacton_right_click
on_hiton_left_click
on_tickon_game_tick
on_removeon_destroy
on_arrow_hiton_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 .bbmodel or .fmmodel for 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 Particle enum 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 count is at least 1.
  • Some particles (like DUST) need specific extra data that the base spawn_particle() may not support. Use standard particles like FLAME, HEART, HAPPY_VILLAGER, NOTE, ENCHANT, etc.

9. Sound does not play

  • Verify the sound name is a valid Bukkit Sound enum 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:

  1. First spawn: When a prop spawns and no sibling .yml file exists, FMM creates the config asynchronously. This means the file is written in a background thread and is not immediately available.

  2. Default values: The generated config has isEnabled: true and an empty scripts: [] list.

  3. 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.

  4. 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.

  5. Config location: The .yml file 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
Quick setup workflow
  1. Place model in models/
  2. Place script in scripts/
  3. Spawn the prop once (generates the .yml)
  4. Edit the .yml to add scripts: [my_script.lua]
  5. 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 errorWhat the console tells you
attempt to call nilYou tried to call a method or function that doesn't exist. Check for typos and : vs . usage.
index expected, got nilYou tried to access a field on something that is nil. Check that earlier code initialized it.
attempt to indexYou tried to access a property on a nil or invalid value.
bad argumentA function received the wrong type of argument. The message shows the expected vs. actual type.
TimeoutYour script took Xms in 'hook_name' (limit: 50ms) -- script disabled to prevent lag.
tip

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. Use context.prop.current_location (a field, not a method).
  • context.prop:set_animation("open") -- does not exist. Use context.prop:play_animation("open", true, true).
  • context.event:setCancelled(true) -- does not exist. Use context.event.cancel().
  • context.cooldowns:check_local(...) -- does not exist in FMM. Use context.state with scheduler:run_later() for cooldowns.
  • context.player -- does not exist in FMM prop scripts. Use context.world:get_nearby_players() to find players near the prop.
  • context.boss -- does not exist. That is EliteMobs. Use context.prop in 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:

  1. Write a file with only api_version = 1 and on_spawn that logs a message.
  2. Add the script to a prop config and verify the log message appears.
  3. Add on_right_click and log when the prop is clicked.
  4. Add on_left_click with context.event.cancel() for invulnerability.
  5. Play an animation on right-click.
  6. Add a sound on right-click.
  7. Add state and toggle behavior.
  8. Add a zone watch for proximity detection.
  9. 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