Wiki 9Minecraft Wiki 9Minecraft

Functions (.mcfunction) in Bedrock Behavior Packs

Write, organize, and trigger multi-line command scripts inside a Behavior Pack.

6Sections
5Code examples
4Known errors

A function in Minecraft Bedrock is a plain text file with the .mcfunction extension that holds a list of slash commands. When triggered, the game executes every command in the file sequentially inside a single game tick. You reach for functions whenever command block chains become too messy to debug, when you need logic that travels with your behavior pack across worlds, or when you want entity events and animations to fire multiple commands at once.

Folder structure and syntax rules

All function files live inside the functions folder at the root of your Behavior Pack. The file path relative to that folder becomes the identifier you type in-game to run it. A file placed at BP/functions/arena/reset.mcfunction is called with /function arena/reset. Subfolders can be nested as deeply as your project requires, and using them to group logic by feature—such as /function shop/buy_sword or /function boss/phase_two—is standard practice on larger packs. File and folder names should stick to lowercase letters, numbers, and underscores; uppercase characters and spaces cause lookup failures on several platforms.

Inside the .mcfunction file, each line is a single command written without the leading forward slash. If you include a slash at the start of a line, the parser will reject that line and throw a syntax error in the content log. Blank lines are ignored, and any line beginning with a hash symbol (#) is treated as a comment. Comments have zero runtime cost, so you can document your scoreboard math or tag conventions directly above the commands that use them.

One strict limitation to keep in mind is that a single command cannot span multiple lines. Even if you are constructing a lengthy /tellraw JSON payload or a chained /execute statement, the entire command must sit on one unbroken line. A single function file can execute up to 10,000 commands per invocation, including any commands run by nested /function calls triggered from within that file. If your logic exceeds that cap—most often because a function calls itself unconditionally—Bedrock aborts the remaining commands for that tick to prevent the server thread from freezing.

BP/functions/arena/start_match.mcfunction
# Reset match timer and clear temporary state tags
scoreboard players set @a[tag=in_arena] match_timer 300
tag @a[tag=in_arena] remove ready_check

# Teleport queued players into the arena ring and set adventure mode
execute as @a[tag=queued] at @s run tp @s 0 65 0 facing 0 65 10
gamemode a @a[tag=queued]
tag @a[tag=queued] add in_arena
tag @a[tag=in_arena] remove queued

# Announce start and play sound
title @a[tag=in_arena] title Match Started!
 execute as @a[tag=in_arena] at @s run playsound random.levelup @s ~ ~ ~ 1.0 1.0

Manifest version and command syntax changes

How Bedrock parses the commands inside your .mcfunction files depends directly on the min_engine_version array inside your Behavior Pack's manifest.json. When Mojang overhauled the /execute command in version 1.19.50 (replacing the old execute <target> ~ ~ ~ <command> syntax with the modern chained subcommands like as, at, positioned, and if), they tied function parsing to this manifest field so older packs would not break.

If your manifest's min_engine_version is set below 1.19.50—for example, [1, 16, 0]—the game forces all functions in that pack through the legacy command parser. Modern syntax like execute as @s at @s run say hi will fail to parse in that pack, even if you are playing on the latest version of Minecraft. Conversely, setting min_engine_version to [1, 19, 50] or higher unlocks the new execute syntax, modern block state bracket notation (such as ["minecraft:facing_direction"="north"]), and recent target selector arguments.

When starting a new project, always set min_engine_version in your BP/manifest.json to match a recent release so your functions match the syntax you test in the chat window. If you are maintaining an older pack and bump this version number up, you will need to audit every .mcfunction file in the pack and rewrite legacy /execute lines and numeric block data values into block states.

BP/manifest.json
{
  "format_version": 2,
  "header": {
    "name": "Arena Functions Pack",
    "description": "Behavior pack using modern command syntax in functions",
    "uuid": "a1b2c3d4-e5f6-4a5b-8c9d-0e1f2a3b4c5d",
    "version": [1, 0, 0],
    "min_engine_version": [1, 20, 80]
  },
  "modules": [
    {
      "type": "data",
      "uuid": "f1e2d3c4-b5a6-4f5e-8d9c-0b1a2f3e4d5c",
      "version": [1, 0, 0]
    }
  ]
}

Execution context and sequential state

Every line in a function inherits the execution context—who @s is, where ~ ~ ~ is located, and which dimension is active—from whatever triggered the function. If a player types /function arena/start_match in chat, @s is that player and ~ ~ ~ is their current feet position. If a command block runs the function, @s is empty (no entity) and ~ ~ ~ is the center of the command block. If you call a function via execute as @e[type=minecraft:armor_stand,name=marker] at @s run function util/spawn_ring, then every single line inside util/spawn_ring.mcfunction starts with @s bound to that armor stand and ~ ~ ~ at the armor stand's coordinates.

A critical detail is that moving the executing entity inside a function does not automatically update the execution position for the lines that follow. Suppose line 1 runs tp @s ~ ~10 ~ to move the entity ten blocks up, and line 2 runs setblock ~ ~-1 ~ stone. Line 2 will place the stone block one block below the original starting position, not below the new teleported position, because the function's context location was locked in when the function started.

Unlike location, entity tags and scoreboard values do update immediately between lines inside the same tick. This makes tags and scores the primary way to build branching logic, loops, and temporary variables inside a function. You can tag a random player on line 1, run five different commands targeting @a[tag=chosen] on lines 2 through 6, and remove the tag on line 7, knowing that all of it resolves atomically before the next frame renders.

Trigger sourceWho is @sWhere is ~ ~ ~Typical use case
Player chat (/function)The player who ran the commandThe player's position at invocationManual testing, admin tools, debug utilities
tick.jsonNo entity (@s selectors fail)World spawn (0, 0, 0 in Overworld)Global timers, ambient checks, scanning all players via @a
Entity animation controllerThe entity running the controllerThe entity's current positionCustom mob attacks, player state transitions, item abilities
execute as <target> at @s run function ...Each matched target entityThat specific entity's positionSubroutines called from another function or command block

Running functions every tick with tick.json

To run one or more functions automatically on every game tick (20 times per second) without placing repeating command blocks in the world, create a file named tick.json directly inside the BP/functions/ folder. This file takes a single JSON object with a values array listing the paths of the functions you want to schedule, written without the .mcfunction extension.

Functions listed in tick.json run at the world level with no executing entity and no meaningful coordinate origin. Because @s evaluates to nothing in this context, every command inside a ticked function must either specify an explicit target selector like @a or @e, or establish a context using execute as <target> at @s run .... If you need a position in the world, anchor the command to an entity first rather than relying on ~ ~ ~.

Because 20 executions per second adds up quickly, avoid running expensive selectors—such as scanning every entity in the world with @e[type=!player] without a distance limit—on every line of a ticked function. A common optimization pattern is to keep a single main.mcfunction in tick.json that increments a scoreboard dummy timer each tick, and only calls heavier sub-functions when that score hits specific intervals (for example, once every 20 ticks for once-per-second logic).

BP/functions/tick.json
{
  "values": [
    "core/main_loop",
    "arena/timer_tick"
  ]
}

Triggering functions from entities and animations

Outside of tick.json and manual chat commands, the most reliable way to fire functions in a survival add-on is through Behavior Pack animation controllers and animations. Despite their name, BP animations do not move bones or render visuals; they are timelines and state machines attached to an entity that can execute slash commands and functions when a state is entered, exited, or reached on a timeline.

When a function is called from an animation controller's on_entry or on_exit array, the game automatically sets @s to the entity that owns the controller and ~ ~ ~ to that entity's current coordinates. This avoids having to search for the entity with tags or radius selectors. You can check Molang queries inside the controller—such as q.is_sneaking or q.is_sheared—and transition into a state that fires a single /function call containing dozens of commands.

For permission-safe execution, verify how the entity triggers the command. Functions run from Behavior Pack animations and animation controllers execute with sufficient privileges to run standard game commands even when cheats are turned off in the world settings, provided the Behavior Pack itself is active on the world. Below is a Behavior Pack animation controller that runs a custom function whenever an entity starts sneaking.

BP/animation_controllers/sneak_ability.ac.json
{
  "format_version": "1.10.0",
  "animation_controllers": {
    "controller.animation.sneak_ability": {
      "initial_state": "standing",
      "states": {
        "standing": {
          "transitions": [
            {
              "sneaking": "q.is_sneaking"
            }
          ]
        },
        "sneaking": {
          "on_entry": [
            "/function abilities/cloak_activate"
          ],
          "on_exit": [
            "/function abilities/cloak_remove"
          ],
          "transitions": [
            {
              "standing": "!q.is_sneaking"
            }
          ]
        }
      }
    }
  }
}

Branching and loops with recursive functions

Because .mcfunction files have no native if/else keywords or while loops, control flow is built by combining /execute if or /execute unless with calls to other functions—or by having a function call itself recursively. When you need a block of ten commands to run only if a player has at least 50 gold on a scoreboard, instead of prefixing all ten lines with the same execute if score check, you write a single execute if score @s gold matches 50.. run function shop/complete_purchase line and put the ten commands inside complete_purchase.mcfunction.

You can also build genuine single-tick loops for tasks like raycasting (stepping forward 0.5 blocks at a time to check for a solid block or hitbox) or clearing a variable number of items. A raycast function moves its execution context forward using execute positioned ^ ^ ^0.5, checks whether the new spot is air, and calls itself again inside that same execute chain. Because the context changes on the execute line that invokes the next function call, the recursive call inherits the shifted coordinates.

Always pair recursive function calls with a hard termination condition, such as a scoreboard counter that counts down on every step and guards the recursive call with if score @s steps matches 1... If your block check fails to stop the loop (for example, the player looks straight up into the sky where every block is air), an unguarded recursive function will slam into the 10,000-command limit and halt with an error.

BP/functions/spells/raycast_step.mcfunction
# Spawn a particle at the current step position
particle minecraft:endrod ~ ~ ~ 

# Decrement remaining range counter on the caster (@s)
scoreboard players remove @s ray_steps 1

# Check for target hit within 1.2 blocks (excluding the caster)
execute as @e[r=1.2,tag=!caster] run damage @s 6 magic entity @p[tag=caster]
execute if entity @e[r=1.2,tag=!caster] run scoreboard players set @s ray_steps 0

# Step forward 0.5 blocks along look vector if block is air and steps remain
execute if score @s ray_steps matches 1.. positioned ^ ^ ^0.5 if block ~ ~ ~ air run function spells/raycast_step

Which versions this applies to

Available in all stable Bedrock versions since 1.8.0. No experimental toggles are required. Modern /execute syntax and block state bracket notation inside functions require min_engine_version [1, 19, 50] or higher in BP/manifest.json.

When it does not work

What you seeWhyThe fix
Function 'name' was not found, or 'Failed to execute function' when running /function in chat.Either the file is not inside BP/functions/, has a .txt extension hidden by your operating system (such as test.mcfunction.txt), or contains at least one line that failed to parse on pack load. In Bedrock, if a single line in a .mcfunction file has a syntax error, the game rejects the entire file and acts as if the function does not exist.Enable 'Content Log GUI' or 'Content Log File' in Creator Settings, reload the world, and check the log output. It will name the exact .mcfunction file and line number that failed to parse. Also check that no line in the file starts with a forward slash (/).
Content Log shows: 'Syntax error: Unexpected "as": at "execute >>as<< @a..."' inside a function, even though the exact same command works in the chat bar.Your Behavior Pack's manifest.json has a min_engine_version lower than [1, 19, 50], which locks all functions in the pack to the legacy pre-1.19.50 command parser.Open BP/manifest.json and update "min_engine_version" inside the header object to at least [1, 19, 50] (or your current game version, such as [1, 20, 80]), then reload the world.
Commands inside a function listed in tick.json fail silently when using @s or place blocks around (0, 0, 0) instead of near players.tick.json runs functions from the server level with no entity assigned to @s and the position anchored at the world origin.Wrap commands inside ticked functions with an explicit context selector, such as 'execute as @a at @s run ...', before referencing @s or relative coordinates (~ ~ ~).
Teleporting @s on one line of a function does not change where ~ ~ ~ is on the next line of the same function.The execution position of a function is fixed at the moment the function is called. Moving an entity during the function changes the entity's coordinates, but not the function's execution origin.After teleporting @s, prefix subsequent position-dependent commands with 'execute at @s run ...' to re-anchor the execution location to the entity's new position.

Reference, straight from the game files

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