Wiki 9Minecraft Wiki 9Minecraft

Troubleshooting JavaScript in Bedrock Add-Ons

Diagnose and fix silent failures, manifest mismatches, and runtime crashes in GameTest and Scripting API.

6Sections
2Code examples
4Known errors

When a JavaScript behavior pack fails, Minecraft Bedrock often drops the script silently or shows a wall of red text on world load. Knowing where to read the Content Log, how to verify manifest dependencies, and why the game rejects certain operations during specific ticks will save you hours of blind restarts. Reach for this guide whenever your entry file refuses to execute or an event callback throws an unexpected runtime exception.

Enabling the Content Log

Before touching your code, turn on Minecraft's built-in error reporting. Open Settings, navigate to the Creator tab, and enable both Content Log File and Content Log GUI. When you load into a world, pressing the configured shortcut opens a history view showing every JSON parse warning and JavaScript stack trace generated during the session.

Inside your script, standard console methods route directly to this log. While console.log outputs standard informational text that only appears when you open the log screen, console.warn and console.error will trigger an immediate on-screen toast notification if the GUI toggle is active. Use console.warn liberally to verify whether your entry file is actually being parsed by the engine.

Manifest and Entry Point Failures

If nothing prints to the Content Log at all, the engine is not loading your script module. The most common cause is a structural mistake in the behavior pack manifest. A script pack requires a specific module inside the modules array with its type set to script, its language set to javascript, and an entry field pointing to your main file relative to the behavior pack root.

You must also declare every native module you import inside the dependencies array using module_name and version. If your script imports from @minecraft/server, that exact string must appear in the manifest. Note that using versions ending in -beta requires the Beta APIs experimental toggle to be enabled on the world, whereas stable semantic versions like 1.12.0 run in normal worlds without experiments.

BP/manifest.json
{
  "format_version": 2,
  "header": {
    "name": "Script Debug Pack",
    "description": "Example manifest for scripting",
    "uuid": "a1b2c3d4-e5f6-4a5b-8c9d-0e1f2a3b4c5d",
    "version": [1, 0, 0],
    "min_engine_version": [1, 21, 0]
  },
  "modules": [
    {
      "type": "script",
      "language": "javascript",
      "uuid": "f1e2d3c4-b5a6-4f5e-8d9c-0b1a2f3e4d5c",
      "version": [1, 0, 0],
      "entry": "scripts/main.js"
    }
  ],
  "dependencies": [
    {
      "module_name": "@minecraft/server",
      "version": "1.12.0"
    }
  ]
}

Import Resolution and Syntax Restrictions

Minecraft Bedrock uses the QuickJS engine to run add-on scripts. It supports modern ES6 module syntax, but its file resolver is stricter than Node.js or web bundlers. When importing a local file from another script in your pack, you must include the relative path prefix (such as ./) and the explicit .js file extension. Omitting the extension causes the module loader to fail before a single line of your code runs.

If you write your scripts in TypeScript, remember that the game cannot read .ts files directly. Ensure your compiler outputs standard JavaScript into the BP/scripts/ folder and that your manifest points to the compiled .js output, not the source files.

Import StatementValid in Bedrock?Notes
import { world } from "@minecraft/server";YesNative engine module; must match manifest dependencies.
import { helper } from "./utils/helper.js";YesExplicit relative path with the .js extension.
import { helper } from "./utils/helper";NoFails with a module resolution error due to missing .js extension.
import { helper } from "utils/helper.js";NoFails because it lacks the leading ./ or ../ relative prefix.

Read-Only Mode and BeforeEvents

A frequent crash for intermediate scripters happens when attempting to modify the world inside a beforeEvents callback. In @minecraft/server, beforeEvents fire prior to an action occurring, allowing you to read data or set data.cancel = true. During this phase, the world state is locked in read-only mode. Calling methods that mutate state—such as spawning entities, modifying inventory items, or setting blocks—will throw a read-only privilege error.

To run state-changing logic in response to a beforeEvent, wrap your mutation calls inside system.run(). This defers the execution until the main tick phase later in the same tick, where the engine unlocks world modification privileges.

BP/scripts/main.js
import { world, system } from "@minecraft/server";

world.beforeEvents.itemUse.subscribe((eventData) => {
  const player = eventData.source;
  const item = eventData.itemStack;

  if (item.typeId === "minecraft:stick") {
    // Cancel the default item use immediately in the beforeEvent
    eventData.cancel = true;

    // Defer world modification to the modifiable phase of the tick
    system.run(() => {
      try {
        player.dimension.spawnEntity("minecraft:lightning_bolt", player.location);
      } catch (err) {
        console.warn("Failed to spawn entity: " + err);
      }
    });
  }
});

Stale Entity References and Unloaded Chunks

Another major source of runtime exceptions is holding onto an Entity or Block reference across multiple ticks. If you store an entity reference in a global variable or pass it into system.runTimeout(), that entity might die, despawn, or walk into an unloaded chunk before your delayed code runs. Calling any method or reading a property like location on an invalidated object throws an exception.

Always check the isValid() method on an entity before interacting with it inside delayed callbacks or interval loops. Similarly, when querying blocks at distant coordinates, dimension.getBlock() will return undefined if the target chunk is not currently loaded in memory. Guard your block logic with an undefined check before reading block permutations or tags.

Watchdog Timeouts and Infinite Loops

Minecraft runs scripts synchronously on the server thread and enforces a strict execution time limit per tick via a script watchdog. If a single tick takes too long—usually because of a massive while loop, recursive function, or scanning tens of thousands of blocks at once—the watchdog terminates your entire script module for the remainder of the world session to prevent the game from freezing.

When you need to perform heavy work, such as building a large structure or searching a wide area, use JavaScript generator functions (function*) paired with system.runJob(). The engine will automatically spread the yielded steps across multiple ticks, keeping frame rates stable and keeping the watchdog happy.

Which versions this applies to

Applies to Minecraft Bedrock 1.20.0 and newer using the modern @minecraft/server API. Stable module versions run in any world; versions with a -beta suffix require the Beta APIs experimental toggle enabled in World Settings.

When it does not work

What you seeWhyThe fix
ReferenceError: error loading module 'scripts/main.js'A local import statement inside main.js or one of its children is missing the .js file extension, or the file path casing does not match the actual file on disk.Check every import line in your scripts and ensure relative imports start with ./ or ../ and end with .js.
Native function [Entity::addEffect] does not have required privileges.You called a method that modifies the world state directly inside a beforeEvents subscription callback while the engine is in read-only mode.Wrap the modifying code inside a system.run(() => { ... }) block so it executes during the normal tick phase, or switch your listener to world.afterEvents if you do not need to cancel the event.
Failed to load script module: dependency '@minecraft/server' version '1.99.0' is not supported.The version string requested in BP/manifest.json does not exist in your current game version, or it is a -beta version and the Beta APIs toggle is turned off on the world.Lower the module version in manifest.json to a stable release supported by your client, or enable the Beta APIs experiment in the world settings if using a beta version.
Watchdog termination: Script execution took too long (exceeded time threshold).A loop or heavy computation blocked the main thread for too many milliseconds in a single tick.Break the loop into smaller chunks across ticks using system.runInterval() or convert the loop into a generator function and pass it to system.runJob().

Reference, straight from the game files

Read out of the game itself, so the ids match the version you are building for.