Five Bedrock add-on bugs that fail without an error

An invisible horse, legs running four times too fast, a mob that forgot how to jump, and a magic pack that broke without being touched. Five Minecraft Bedrock add-on bugs I hit building LLS Equestrian and the Magic Pack, and how to catch them.

· 6 min read

A horse from LLS Equestrian working on a lunge line

The worst bugs in a Bedrock add-on aren’t the ones that crash. They’re the ones where the game carries on as if nothing happened: no error on screen, no crash, just a mob that’s gone invisible or a feature that quietly stopped working. Here are five I hit while building LLS Equestrian and the Magic Pack, what each looked like, and how I catch them now.

If you’re a player rather than a modder, the one thing to take from this: when an add-on misbehaves, switch on the Content Log (Settings → Creator). Every one of these leaves a trace there, and nowhere else. There’s a guide to reading it.

1. math.max with three arguments makes the whole mob invisible

What happened: after adding a new jump animation, every horse in the world went invisible. So did the horse spawn egg. Every item from the same pack still showed fine.

The cause: one animation line used math.max(a, b, c). Molang’s maths functions take a fixed number of arguments: math.max takes exactly two. With three, Bedrock logs “Unexpected number of parameters… expected 2, found 3” and throws out the entire client entity: model, textures, animations and the egg icon with it.

Why it got past me: I build the animations with a script, and it previews them by translating Molang to JavaScript. JavaScript’s Math.max takes any number of arguments, so the preview ran perfectly.

The fix: nest the calls, math.max(math.max(a, b), c). The build script now checks the argument count of every Molang function call before it writes anything.

2. Reading a variable before it’s set stops everything after it

What happened: some horses rendered pink, some invisible, and the game lagged badly. The Content Log was scrolling thousands of “unknown variable” errors a second.

The cause: in an entity’s pre_animation script, one line read a variable that was only set further down. On Bedrock, reading an unset variable doesn’t give you zero: it raises an error that stops the rest of the script, every frame. So every variable after that line was never set, including the one that picks the coat texture. No texture index meant the pink “missing texture” colour.

Why it got past me: in JavaScript a missing value is just undefined. And a test run skipped the line, because it sat behind a condition (the horse lying down) that the test never met.

The fix: any variable read above the line that sets it needs a starting value in initialize. The build now checks this statically: it walks the lines in the order the game runs them (initialize, then pre_animation, then the animations) and refuses to build if anything is read before it’s written.

Tip: when you see a flood of “unknown variable” errors, find the variable that’s set earliest in the script. The broken line is just above it.

3. modified_distance_moved isn’t in blocks

What happened: in previews, the walk and trot looked right. In game, the legs went like a sewing machine. I counted six head bobs to a real horse’s one or two. Every “make it slower” change I tried made the previews wrong and barely touched the game.

Eight frames of the gallop cycle from the horse animation preview
The gallop cycle from the preview renderer. It looked right here the whole time; the game was the one running fast.

The cause: the stride was timed off q.modified_distance_moved, as if it counted blocks travelled. It doesn’t. It’s vanilla’s limb-swing counter: it runs at about four times the distance moved and caps at 20 per second, which it hits at about five blocks a second. So the walk and trot were running four times too fast, and the canter and gallop about twice (because of the cap).

Why it got past me: the previews fed the animation the real speed, not what the game actually reports. Preview and game silently disagreed.

The fix: time stride-locked animation from q.ground_speed, which really is blocks per second. The bigger lesson: an offline preview has to simulate the game’s own numbers the way the game produces them, odd scaling included, or it will look right while the game looks wrong.

4. Removing a component group deletes things it didn’t add

What happened: horses that had never worn a work harness lost their jump after lying down, rolling or being brushed. Leaving and rejoining the world brought it back.

The cause: in a Bedrock entity file, component groups are added on top of each other, but removing one deletes every component it names, outright. It doesn’t fall back to the version in the base components, or in another group that’s still active.

The “lie down” event’s remove list included the work-harness group “just in case”. That group sets jump strength to zero, so it names the jump component. Removing it removed the horse’s jump, whether the horse had ever been harnessed or not. Rejoining fixed it only because a script re-applies jump strength on load.

The fix:

  • Keep each shared component in exactly one group at a time.
  • Swap groups in one event: remove the old and add the new together.
  • Never put a group on a remove list “just in case”. Removing a group a mob doesn’t have still deletes the components it names.

5. An update you didn’t make breaks a pack you didn’t touch

What happened: the Magic Pack was finished and working. A few days later the frost staff stopped hitting anything, healing checks always failed, and knockback stopped completely. Nobody had changed a line of its code.

The cause: the Magic Pack ships as one combined pack built from several mods, and the combined pack runs all of their scripts under one version of Bedrock’s scripting API, the highest any of them asks for. When another mod in the bundle moved to version 2.x, the older magic code started running under 2.x too. Two things changed in 2.0:

  • isValid went from a method to a property. Calling entity.isValid() now throws “not a function”.
  • applyKnockback(dirX, dirZ, strength, up) changed shape to applyKnockback({ x, z }, up). The old form throws.

Both calls were wrapped in try/catch, so instead of crashing the features just stopped. The log had 170,000 “not a function” lines from the frost staff alone.

The fix: a small compatibility file with alive() and knockback() helpers that work under both versions. And a rule: after raising the scripting version of anything in a bundle, search the Content Log for not a function and the older code for .isValid() and applyKnockback(.

The pattern

All five have the same shape: something that checks my work agreed with me, and the game didn’t. A preview in JavaScript, a test that skipped a branch, a try/catch that swallowed an error. What catches them:

  1. Read the Content Log after every change, not only when something breaks.
  2. Make previews and tests behave like the game, including its strict and strange parts.
  3. Check statically what you can. Argument counts and read-before-set are both caught before a single file is written now.

The horses all of this went into are in LLS Equestrian, and the staves in the Magic Pack. Both are free downloads for Bedrock.

The mods

More from the devlog

← All devlog