Wiki 9Minecraft Wiki 9Minecraft

NBT Libraries and External Binary Parsers

How external tools read and write Bedrock Edition's little-endian NBT and LevelDB files.

5Sections
2Code examples
3Known errors

Named Binary Tag (NBT) is the binary format Minecraft uses to store structured data for worlds, entities, items, and saved structures. While Bedrock add-ons primarily use JSON and the Script API at runtime, add-on developers reach for external NBT libraries when building pipeline tools, generating `.mcstructure` files programmatically, or batch-editing world data outside the game.

Bedrock NBT vs. Runtime Add-on Data

It is important to understand a core boundary in Bedrock Edition: you cannot read or write raw NBT tags directly from JSON behavior files or the `@minecraft/server` Script API during gameplay. Unlike commands in other editions that accept inline curly-brace NBT strings, Bedrock exposes game state through structured JSON components, Entity Properties, and Script API `DynamicProperties`.

NBT libraries do not run inside your behavior pack. Instead, they run in external environments—typically Node.js, Python, or Rust—on your development machine. You use them to inspect or generate binary files that you subsequently place into your behavior pack (such as `BP/structures/*.mcstructure`), or to manipulate a test world's `level.dat` and LevelDB chunk database before packaging a map.

Bedrock's Binary NBT Dialects

If you are configuring an external NBT library to read or write Bedrock files, you must select the correct binary format. While the underlying tag types (Byte, Short, Int, Long, Float, Double, ByteArray, String, List, Compound, IntArray, LongArray) are identical across Minecraft editions, Bedrock encodes the bytes differently than Java Edition.

Disk files in Bedrock—most notably `BP/structures/*.mcstructure` and `level.dat`—use uncommitted Little-Endian NBT (`little`). Furthermore, `level.dat` is not a compressed archive; it is an uncompressed Little-Endian NBT compound preceded by an 8-byte binary header (4 bytes for the storage version, 4 bytes for the payload length). Network packets and certain internal LevelDB entries use a third variant called `littleVarint`, which compresses integers using ZigZag VarInt encoding. Any library you choose must explicitly support the `little` format to generate valid behavior pack files.

Tag IDType NamePayload DescriptionBedrock Usage Notes
1TAG_ByteSigned 8-bit integerAlso used for boolean flags (`0` or `1`).
2TAG_ShortSigned 16-bit integer (LE)Common for entity air supply, potion durations, or damage values.
3TAG_IntSigned 32-bit integer (LE)Standard integer for coordinates, counts, and format versions.
4TAG_LongSigned 64-bit integer (LE)Used for unique entity IDs (`UniqueID`) and world seeds.
5TAG_Float32-bit IEEE-754 float (LE)Used for entity rotation, health, and motion vectors.
8TAG_StringUTF-8 string prefixed by 16-bit LE lengthStores identifiers like `minecraft:stone` or custom dynamic property strings.
9TAG_List1-byte tag ID, 32-bit LE length, followed by homogeneous payloadsUsed for coordinate arrays (`size`, `structure_world_origin`) and inventory slots.
10TAG_CompoundSequence of named tags terminated by `TAG_End` (`0x00`)The root container of every `.mcstructure` file and block state map.

Major Libraries for Add-on Tooling

Three open-source libraries dominate Bedrock tool development across JavaScript and Python ecosystems. Because Regolith (the standard Bedrock add-on compiler) runs on Node.js and Python, these libraries integrate directly into automated build filters.

In the Node.js ecosystem, `prismarine-nbt` is the standard parser. It automatically detects big-endian, little-endian, and varint encodings when parsing, and allows you to specify `'little'` when serializing buffers back to disk. In the Python ecosystem, `amulet-nbt` (maintained by the Amulet Map Editor team) and `nbtlib` both offer full support for uncompressed Little-Endian files. If your tool needs to modify world chunks rather than loose `.mcstructure` files, you will also need a LevelDB wrapper that supports Mojang's Zlib-raw compression, as NBT libraries only parse the byte buffers extracted from the database records.

Reading and Modifying .mcstructure Files in Node.js

The most practical use for an NBT library during add-on development is batch-processing `.mcstructure` files inside your behavior pack. For example, if you rename a custom block across your add-on, re-exporting dozens of structure files by hand in-game with a Structure Block is slow and error-prone. A short Node.js script using `prismarine-nbt` can parse the binary file, walk the block palette, replace the identifier string, and write the updated binary straight back into `BP/structures/`.

When serializing the modified object back to a Buffer, you must pass `'little'` as the second argument to `nbt.writeUncompressed()`. If you omit this argument, the library defaults to big-endian (`'big'`), and Bedrock will silently fail to load the structure.

tools/replace_palette_block.js
const fs = require('fs');
const nbt = require('prismarine-nbt');

async function renameBlockInStructure(filePath, oldId, newId) {
  const rawBuffer = fs.readFileSync(filePath);
  
  // Parse the binary .mcstructure buffer (auto-detects 'little' endian)
  const { parsed, type } = await nbt.parse(rawBuffer);

  // Navigate to structure -> palette -> default -> block_palette
  const palette = parsed.value.structure.value.palette.value.default.value.block_palette.value.value;

  let modifiedCount = 0;
  for (const blockEntry of palette) {
    if (blockEntry.name && blockEntry.name.value === oldId) {
      blockEntry.name.value = newId;
      modifiedCount++;
    }
  }

  if (modifiedCount > 0) {
    // Write back as uncompressed Little-Endian NBT
    const outputBuffer = nbt.writeUncompressed(parsed, 'little');
    fs.writeFileSync(filePath, outputBuffer);
    console.log(`Updated ${modifiedCount} palette entries in ${filePath}`);
  }
}

renameBlockInStructure(
  'BP/structures/dungeon_room.mcstructure',
  'my_pack:old_brick',
  'my_pack:cracked_brick'
);

Valid Block Definitions for Generated Structures

When generating or modifying block palettes in a `.mcstructure` via an NBT library, the `name` and `states` tags in your binary compound must exactly match the block definition in your behavior pack. If your script injects a state name or data type that does not exist in the behavior pack JSON, Bedrock will either revert the block to its default permutation or replace it with the `minecraft:info_update` block.

Below is a standard behavior pack block definition whose state (`my_pack:facing_direction`) is stored as a 32-bit `TAG_Int` inside the `states` compound of a `.mcstructure` palette entry.

BP/blocks/cracked_brick.json
{
  "format_version": "1.20.80",
  "minecraft:block": {
    "description": {
      "identifier": "my_pack:cracked_brick",
      "states": {
        "my_pack:facing_direction": [0, 1, 2, 3]
      }
    },
    "components": {
      "minecraft:destructible_by_mining": {
        "seconds_to_destroy": 2.0
      },
      "minecraft:destructible_by_explosion": {
        "explosion_resistance": 6.0
      },
      "minecraft:geometry": "minecraft:geometry.full_block"
    }
  }
}

Which versions this applies to

Bedrock's Little-Endian NBT format (`format_version: 1` for `.mcstructure` files) applies to all modern Bedrock versions (1.16.0 through 1.21+). No experimental toggles are required to load `.mcstructure` files modified by external NBT tools.

When it does not work

What you seeWhyThe fix
Running `/structure load` in-game prints "Failed to load structure" or places nothing, even though the `.mcstructure` file exists in `BP/structures/`.Your NBT library serialized the file using Gzip compression or Big-Endian byte order. Bedrock `.mcstructure` files must be uncompressed and strictly Little-Endian.Ensure your serializer explicitly disables Gzip compression and sets endianness to little-endian (for example, `nbt.writeUncompressed(data, 'little')` in `prismarine-nbt` or `compressed=False, little_endian=True` in `amulet-nbt`).
Opening a world after modifying `level.dat` with an NBT script causes Minecraft to show "There was a problem loading this world" or resets world settings.You stripped the 8-byte header at the beginning of `level.dat` or failed to update the byte-length integer in bytes 4–7 after changing the size of the NBT payload.Slice off the first 8 bytes before passing `level.dat` to your NBT parser. When writing the file back, prepend the 4-byte Little-Endian storage version integer followed by the new 4-byte Little-Endian byte length of your serialized NBT buffer.
Blocks placed by a script-generated `.mcstructure` appear as dirt blocks with the text "UPD!" (`minecraft:info_update`) or lose their block states.The NBT tag type used in the structure's `states` compound does not match the state type declared in `BP/blocks/*.json` (for example, writing a `TAG_Int` of `1` for a boolean state instead of a `TAG_Byte`, or omitting required states).Use a `TAG_Byte` (`1` or `0`) for boolean block states, `TAG_Int` for integer range states, and `TAG_String` for string enum states inside the `states` compound of each `block_palette` entry.

Reference, straight from the game files

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