How Bedrock Handles NBT
When you build a Bedrock add-on, you will notice quickly that commands like /give or /summon do not accept raw curly-brace NBT data. You cannot check an entity's inventory or health by matching a raw NBT path in a selector. Instead, Bedrock keeps NBT strictly as an internal storage and serialization format while exposing curated interfaces to creators.
You will encounter actual NBT files in two main places: inside the world save folder (within the db/ LevelDB directory and the root level.dat file) and inside exported .mcstructure files located in your behavior pack's structures/ folder. Because these files are binary and encoded in Little-Endian format, opening them in a standard text editor only shows garbled characters alongside occasional readable strings.
If your goal is to inspect how the vanilla game saves a specific block state, tile entity, or entity definition inside a structure, you read the NBT offline using an external editor or a local utility script. If your goal is to read data while the player is actively playing the world, you use Molang queries, entity properties, or the @minecraft/server Script API.
NBT Tag Types and Binary Layout
Every piece of data in an NBT file is stored as a tag. A tag begins with a single byte that identifies its type, followed by a two-byte length prefix and a UTF-8 string for its name (unless the tag lives inside a List, where tags are unnamed), and finally the payload itself. Bedrock uses Little-Endian byte ordering for almost all disk-based NBT files.
When inspecting a structure or world file in an NBT viewer, you will see these standard tag types organizing the hierarchy. Understanding the numeric types is especially important when modifying block states or entity data offline, because saving a Byte as an Int will cause the game to silently ignore the tag when loading the file.
| Tag ID | Name | What it stores |
|---|---|---|
| 1 | TAG_Byte | 8-bit signed integer (-128 to 127). Also used for boolean true (1) and false (0) values. |
| 2 | TAG_Short | 16-bit signed integer. Often used for health, air supply, or potion durations. |
| 3 | TAG_Int | 32-bit signed integer. Standard whole numbers such as coordinates or tick counters. |
| 5 | TAG_Float | 32-bit floating-point number. Used for rotation angles, motion vectors, and attribute values. |
| 8 | TAG_String | UTF-8 text string prefixed with its byte length, such as identifiers like minecraft:chest. |
| 9 | TAG_List | An ordered array of unnamed tags that all share the exact same Tag ID. |
| 10 | TAG_Compound | A key-value map of named tags of any type, closed by a single 0x00 (TAG_End) byte. |
Anatomy of an .mcstructure File
The most practical reason to read raw NBT during add-on development is to inspect a .mcstructure file in BP/structures/. When you export a structure using a Structure Block in-game, Bedrock writes an uncompressed Little-Endian NBT compound tag to disk. Examining this file shows you the exact state names and values the game expects for blocks, as well as the saved data of any entities captured in the volume.
At the root of a .mcstructure file, you will find a format_version integer, a size list containing three integers (X, Y, and Z bounds), a structure compound, and a structure_world_origin list. Inside the structure compound sits the block_indices list, the entities list, and the palette compound.
The block_indices tag is a list of two lists of integers. The first inner list represents the primary block layer, and the second represents the waterlogging or liquid layer. Each integer is an index pointing to the block_palette inside the palette compound. The palette compound also holds position_data, which maps specific index positions to block_entity_data (such as the contents of a chest) and tick_queue_data.
Reading Data at Runtime with the Script API
Because you cannot read NBT directly while the game is running, the standard way to inspect entity, item, or block data at runtime is through the @minecraft/server Script API. Instead of traversing an NBT tree, you query specific components on the target object.
For example, in a system where you might have read an entity's Health, Pos, and ArmorItems NBT tags in other environments, in Bedrock you read properties directly from the Entity class or fetch its attached components via getComponent(). If you need to store and read your own custom NBT-like data on an entity, item, or the world itself, you use Dynamic Properties via getDynamicProperty() and setDynamicProperty().
The script below demonstrates how to inspect a block and an entity when a player uses an item, reading the runtime equivalents of block states, container inventory, and entity health.
import { world, EquipmentSlot } from "@minecraft/server";
world.afterEvents.playerInteractWithEntity.subscribe((event) => {
const { player, target } = event;
// 1. Reading basic properties (equivalent to Pos, id, CustomName tags)
const id = target.typeId;
const loc = target.location;
const name = target.nameTag;
// 2. Reading health via component (equivalent to Health tag)
const healthComp = target.getComponent("minecraft:health");
const currentHealth = healthComp ? healthComp.currentValue : 0;
// 3. Reading equipped items (equivalent to Equipment/ArmorItems tags)
const equippable = target.getComponent("minecraft:equippable");
let mainhandItem = "empty";
if (equippable) {
const item = equippable.getEquipment(EquipmentSlot.Mainhand);
if (item) {
mainhandItem = `${item.typeId} (x${item.amount})`;
}
}
// 4. Reading custom saved data (Bedrock's replacement for custom NBT)
const customFaction = target.getDynamicProperty("my_pack:faction") ?? "none";
player.sendMessage(
`Target: ${id} (${name}) at ${Math.floor(loc.x)}, ${Math.floor(loc.y)}, ${Math.floor(loc.z)}\n` +
`Health: ${currentHealth} | Mainhand: ${mainhandItem} | Faction: ${customFaction}`
);
});
world.afterEvents.playerInteractWithBlock.subscribe((event) => {
const { player, block } = event;
// Reading block states (equivalent to palette block state NBT)
const permutation = block.permutation;
const allStates = permutation.getAllStates();
const stateString = JSON.stringify(allStates);
// Reading container slots (equivalent to block_entity_data Items list)
const inventory = block.getComponent("minecraft:inventory");
let slotZeroInfo = "No container";
if (inventory && inventory.container) {
const firstItem = inventory.container.getItem(0);
slotZeroInfo = firstItem ? firstItem.typeId : "Empty slot 0";
}
player.sendMessage(`Block: ${block.typeId}\nStates: ${stateString}\nSlot 0: ${slotZeroInfo}`);
});Reading Entity State in JSON with Molang and Filters
If you do not want to use the Script API, you can read many aspects of an entity's internal state directly inside behavior and resource pack JSON files using Entity Filters and Molang queries. Whenever the game loads an entity from disk, it translates its internal NBT tags into runtime data that Molang and filters can evaluate.
For example, to check if an entity is a baby, is tamed, or is holding a specific item, you use an entity filter block inside a component group trigger or an event. If you want to read numeric values—such as health, movement speed, or custom Enum/Int/Float Entity Properties—you use Molang queries like query.health or query.property().
The following entity behavior snippet shows how to read both a vanilla state (whether the entity is currently on fire) and a custom synced Entity Property inside an environment sensor, triggering an event without ever touching raw NBT.
{
"format_version": "1.20.80",
"minecraft:entity": {
"description": {
"identifier": "my_pack:custom_guard",
"is_spawnable": true,
"is_summonable": true,
"properties": {
"my_pack:alert_level": {
"type": "int",
"range": [0, 3],
"default": 0,
"client_sync": true
}
}
},
"components": {
"minecraft:health": {
"value": 20,
"max": 20
},
"minecraft:environment_sensor": {
"triggers": [
{
"filters": {
"all_of": [
{
"test": "on_fire",
"subject": "self",
"value": true
},
{
"test": "int_property",
"domain": "my_pack:alert_level",
"operator": "<",
"value": 2
}
]
},
"event": "my_pack:raise_alert"
}
]
}
},
"events": {
"my_pack:raise_alert": {
"set_property": {
"my_pack:alert_level": 2
}
}
}
}
}Which versions this applies to
Applies to all modern Bedrock versions (1.20.80+ recommended for current Entity Properties and stable @minecraft/server Script APIs). No experimental toggles are required for standard .mcstructure files, Entity Properties, or the demonstrated Script API methods.
When it does not work
| What you see | Why | The fix |
|---|---|---|
| Syntax error: Unexpected "{" at ... in /give or /summon command | You tried to pass raw Java-style NBT data (such as {CustomName:"Bob"} or {Enchantments:[...]}) in a Bedrock command. Bedrock commands only accept a small set of specific JSON item components (like minecraft:can_place_on, minecraft:can_destroy, minecraft:item_lock, and minecraft:keep_on_death). | To spawn pre-configured items or entities with custom names, enchantments, and inventories, save them inside a 1x1x1 .mcstructure file in BP/structures/ and load them in-game using the /structure load command, or configure them dynamically using the Script API. |
| External NBT tool fails to open level.dat or reports an invalid header | Unlike standard gzipped Big-Endian NBT files, Bedrock's level.dat is uncompressed Little-Endian NBT prefixed with an 8-byte header (4 bytes for the storage version integer, followed by 4 bytes indicating the payload byte length). | Use an NBT editor that explicitly supports Bedrock Edition's Little-Endian format and strips or accounts for the 8-byte header on level.dat, or skip the first 8 bytes if parsing the buffer in your own external tool. |
| Edited .mcstructure file fails to load or ignores changed block states | When editing a structure's block_palette in an external NBT editor, a numeric state tag was saved with the wrong Tag ID (for example, changing a TAG_Byte boolean state to a TAG_Int), or the version integer on the palette entry does not match the state schema. | Verify the exact Tag ID of every state in the states compound by exporting a fresh structure from the target Bedrock version and matching its tag types byte-for-byte. |
Reference, straight from the game files
Read out of the game itself, so the ids match the version you are building for.
