Choosing a Detection Method
Unlike Java Edition, Minecraft Bedrock does not have a built-in scoreboard criterion that increments automatically when a player dies. Target selectors like @a or @e[type=player] also ignore dead players by default once the death screen appears, which makes pure command-block detection tricky without preparation.
Instead, add-on creators rely on three reliable patterns. If you are comfortable using the Script API, the entityDie after-event is the cleanest approach because it does not require modifying the vanilla player entity file. If you want a pure JSON and command function approach, an animation controller listening to the is_alive Molang query is the traditional choice. Finally, if you need to intercept fatal damage before the player actually dies—for example, to teleport them to a lobby instead of showing the death screen—you must modify BP/entities/player.json and use a damage sensor.
| Method | Modifies player.json? | Runs at Death Location? | Best Used For |
|---|---|---|---|
| Script API (entityDie) | No | Yes | Stacking with other add-ons, reading the killer entity, spawning gravestones |
| Animation Controller | Yes | Yes (on transition) | Pure command/mcfunction packs, running separate logic on death and respawn |
| Damage Sensor (fatal) | Yes | Yes | Cancelling death entirely, custom minigame elimination, instant respawn |
Method 1: Script API (entityDie Event)
The modern and most compatible way to run commands on player death is through the @minecraft/server Script API. By subscribing to world.afterEvents.entityDie, your pack listens for deaths globally without overriding BP/entities/player.json. This means your add-on will not conflict with other packs that alter player behaviour.
When the event fires, the player is already dead, so running a command directly via player.runCommandAsync() can sometimes behave unexpectedly if the command relies on an alive executor. However, the player's dimension and location properties still hold the exact coordinates where they died. Running the command on the dimension object at those coordinates ensures that particle effects, structure loads, or entity summons happen right on the death spot.
The event data also provides a damageSource object. From this, you can read the cause of death (such as fall, lava, or entityAttack) and even get a reference to the killer entity if there was one. This makes it trivial to award kill points to another player or log why a player died.
import { world } from "@minecraft/server";
world.afterEvents.entityDie.subscribe((event) => {
const deadEntity = event.deadEntity;
const damageSource = event.damageSource;
const dim = deadEntity.dimension;
const loc = deadEntity.location;
// Increment a 'deaths' scoreboard objective if it exists
const deathsObj = world.scoreboard.getObjective("deaths");
if (deathsObj && deadEntity.scoreboardIdentity) {
deathsObj.addScore(deadEntity.scoreboardIdentity, 1);
}
// Run commands at the exact death coordinates
const x = Math.floor(loc.x);
const y = Math.floor(loc.y);
const z = Math.floor(loc.z);
dim.runCommandAsync(`particle minecraft:totem_particle ${x} ${y + 1} ${z}`);
dim.runCommandAsync(`playsound random.glass @a ${x} ${y} ${z} 1.0 0.8`);
dim.runCommandAsync(`tellraw @a {"rawtext":[{"text":"${deadEntity.name} perished at ${x}, ${y}, ${z} (Cause: ${damageSource.cause})"}]}`);
}, { entityTypes: ["minecraft:player"] });Method 2: Behaviour Pack Animation Controller
Before the Script API became stable, the standard way to run commands on death was a Behaviour Pack animation controller attached to the player entity. This method is still widely used if you want to keep all your logic inside JSON and .mcfunction files. It works by evaluating the Molang query query.is_alive every tick.
When the player's health drops to zero, query.is_alive becomes false (or 0). An animation controller Watching for !query.is_alive immediately transitions from its default state to a dead state. Any slash commands or function calls listed in the on_entry array of that dead state will execute once at the exact tick and location of death.
A major advantage of this state-machine approach is that you get a free respawn detector at the same time. While the player sits on the 'You Died!' screen, the controller waits in the dead state. The moment they click Respawn, query.is_alive becomes true again, transitioning the controller back to the alive state and firing any commands you placed in the alive state's on_entry or the dead state's on_exit.
{
"format_version": "1.10.0",
"animation_controllers": {
"controller.animation.custom.player_death": {
"initial_state": "alive",
"states": {
"alive": {
"transitions": [
{
"dead": "!query.is_alive"
}
]
},
"dead": {
"on_entry": [
"/scoreboard players add @s deaths 1",
"/function player/on_death"
],
"on_exit": [
"/function player/on_respawn"
],
"transitions": [
{
"alive": "query.is_alive"
}
]
}
}
}
}
}Linking the Animation Controller to the Player
Creating the animation controller file is only half the setup; the game will not run it until you link it inside BP/entities/player.json. You must copy the vanilla player.json file into your behaviour pack and add your controller to the description block.
Inside the description object, register a short name for your controller under the animations key, and then tell the entity to run that short name inside the scripts.animate array. If your player.json already has a scripts.animate array for other mechanics, simply append your short name to the existing list.
{
"format_version": "1.20.0",
"minecraft:entity": {
"description": {
"identifier": "minecraft:player",
"is_spawnable": false,
"is_summonable": false,
"is_experimental": false,
"animations": {
"death_tracker": "controller.animation.custom.player_death"
},
"scripts": {
"animate": [
"death_tracker"
]
}
},
"components": {
}
}
}Running an Function Recipe at the Death Spot
When the animation controller transitions into the dead state and runs /function player/on_death, the execution context (@s and the current command position ~ ~ ~) is still anchored to the dying player at their death coordinates. However, there is an important quirk you must build around: standard target selectors like @a or @p filter out dead entities.
Because the player is technically dead on the tick that on_entry fires, a command like /tp @s or /tag @s add was_killed will work, because @s specifically targets the calling entity regardless of whether they are alive. Conversely, a command like /execute as @a[r=5] will skip the dying player. Always use @s inside your death function to refer to the victim, and use ~ ~ ~ to place blocks, summon entities, or spawn loot at their corpse.
Remember that you cannot give items to or clear items from a dead player's inventory, nor can you apply status effects to them until they respawn. Put inventory recovery or status effect commands inside your on_respawn function instead.
# Runs as @s (the dying player) at their exact death coordinates
summon minecraft:armor_stand "Grave" ~ ~ ~
playsound mob.wither.spawn @a ~ ~ ~ 0.8 0.5
particle minecraft:huge_explosion_emitter ~ ~1 ~
tellraw @a {"rawtext":[{"selector":"@s"},{"text":" dropped their items at their grave!"}]}Method 3: Intercepting Fatal Damage (Fake Death)
In many PvP maps and minigames, you do not want the player to see the vanilla 'You Died!' screen at all. Instead, when they take a blow that would kill them, you want to cancel the damage, heal them, switch them to Spectator mode, and run your elimination commands immediately.
You can achieve this in BP/entities/player.json using the minecraft:damage_sensor component. By adding a trigger with the filter is_fatal, you can catch any lethal damage before the player's health reaches zero. Setting deals_damage to false cancels the killing blow, and the on_damage block fires an entity event on the player.
Once that entity event triggers, you can use the Queue Command entity response (or an animation controller listening for a component group tag) to heal the player, change their gamemode, and announce their elimination. Because deals_damage is false, the player never actually dies, meaning @a selectors and effect commands continue to work on them without interruption.
{
"format_version": "1.20.0",
"minecraft:entity": {
"description": {
"identifier": "minecraft:player",
"is_spawnable": false,
"is_summonable": false,
"is_experimental": false
},
"components": {
"minecraft:damage_sensor": {
"triggers": [
{
"on_damage": {
"filters": {
"test": "is_fatal",
"subject": "self",
"value": true
},
"event": "custom:on_fatal_hit"
},
"deals_damage": false
}
]
}
},
"events": {
"custom:on_fatal_hit": {
"queue_command": {
"command": [
"effect @s instant_health 1 255 true",
"gamemode spectator @s",
"title @s title Eliminated!",
"playsound random.totem @a ~ ~ ~"
]
}
}
}
}
}Which versions this applies to
Applies to Minecraft Bedrock 1.20.0 and newer. Neither the animation controller method, the damage sensor method, nor the world.afterEvents.entityDie Script API event requires any experimental toggles in current stable releases (requires @minecraft/server 1.4.0 or higher in manifest.json for the script method).
When it does not work
| What you see | Why | The fix |
|---|---|---|
| The player loses all normal movement, hunger, or attacks after adding BP/entities/player.json. | Entity files in Bedrock do not merge with vanilla files; they completely replace them. If you create a minimal player.json with only your animation controller or damage sensor and leave the components object empty, the player loses every vanilla component. | Always start by copying the full vanilla player.json from the official Bedrock sample packs for your target version, then add your description scripts or damage_sensor into the existing structure. Alternatively, use the Script API method so you do not need player.json at all. |
| Content Log Error: [Animation][error]-controller.animation.custom.player_death | Failed to execute command... | Commands inside an animation controller's on_entry array must begin with a forward slash (/), whereas commands inside an .mcfunction file or queue_command block must not have a leading slash. Mixing these up causes the parser or command runner to fail. | Ensure every command string inside your animation controller JSON starts with '/', and remove the leading '/' from every line inside your .mcfunction files. |
| Commands in the death function run, but /effect or /give fails with 'No targets matched selector'. | While a player is in the dying animation or sitting on the death screen, the game treats them as a dead entity. Dead entities cannot receive status effects or inventory modifications, and selectors like @p or @a[r=5] ignore them completely. | Use @s to target the dying player for scoreboard and coordinate commands, and move any /give, /clear, or /effect commands to the on_exit array of the dead state so they run after the player clicks Respawn. |
| The Script API entityDie event never triggers and the script does not load. | The behaviour pack manifest.json is missing the script module definition or the @minecraft/server dependency block. | Add a 'script' type module pointing to 'scripts/main.js' and declare '@minecraft/server' under dependencies in your BP/manifest.json. |
Reference, straight from the game files
Read out of the game itself, so the ids match the version you are building for.
