How Bedrock identifies and upgrades packs
Minecraft Bedrock does not care about the folder name of your add-on or the file name of your `.mcpack` or `.mcaddon` archive when deciding how to load a pack. Instead, the engine looks exclusively at the `manifest.json` file at the root of your Behavior Pack (BP) and Resource Pack (RP). Specifically, it reads the `uuid` and `version` fields inside the `header` object.
When a player imports a pack, Minecraft checks its global pack cache (`minecraftpe/games/com.mojang/behavior_packs` and `resource_packs`) to see if a pack with that exact `header.uuid` is already installed. If the UUID is new, it installs the pack fresh. If the UUID already exists, Minecraft compares the incoming `header.version` array against the installed one. If the incoming version is higher, Minecraft installs the new pack alongside the old one in the cache, and any world that does not have its pack version locked will automatically use the highest available version on its next load.
This system only works if you obey the prime rule of Bedrock publishing: once you distribute an add-on to players, you must never change the `uuid` in the `header` of either pack. Generating a new UUID makes Minecraft treat your update as a brand-new, unrelated add-on. Players will end up with two versions of your pack enabled at once, or they will have to remove the old one and apply the new one, which can sometimes strip entity data or reset custom block states in an active save.
Anatomy of a versioned manifest pair
A complete add-on almost always consists of a Behavior Pack and a Resource Pack working together. To make sure they stay paired when you ship an update, both manifests need synchronized version numbers and a strict `dependencies` link. There are four distinct version arrays you have to manage across a typical BP/RP pair, and forgetting to bump even one of them can cause a mismatch.
Here is a production-ready Behavior Pack manifest representing version `1.2.0` of an add-on. Notice how the version `[1, 2, 0]` appears three times: once for the pack itself, once for its internal data module, and once pointing to the required version of the partner Resource Pack.
| Manifest Path | What it does | When you ship an update |
|---|---|---|
| header.uuid | Uniquely identifies the pack to Minecraft and worlds. | Keep identical forever. Never regenerate. |
| header.version | The version of the pack itself as `[major, minor, patch]`. | Increment at least one number every release. |
| header.min_engine_version | Minimum game version required, and locks the engine behavior baseline. | Only raise when you need new vanilla features. |
| modules[].uuid | Identifies the internal data, resources, or script module. | Keep identical forever. Never regenerate. |
| modules[].version | Version of the specific module. | Increment to match `header.version`. |
| dependencies[].uuid | Points to the `header.uuid` of the partner RP or BP. | Keep identical to the partner pack's `header.uuid`. |
| dependencies[].version | Requires a specific version of the partner pack. | Increment to match the partner pack's new `header.version`. |
{
"format_version": 2,
"header": {
"name": "pack.name",
"description": "pack.description",
"uuid": "5c8a7912-34f1-4a9b-8e11-90123456789a",
"version": [1, 2, 0],
"min_engine_version": [1, 20, 80]
},
"modules": [
{
"type": "data",
"uuid": "a1b2c3d4-e5f6-4a5b-8c9d-0e1f2a3b4c5d",
"version": [1, 2, 0]
}
],
"dependencies": [
{
"uuid": "9f8e7d6c-5b4a-4f3e-8d2c-1b0a9f8e7d6c",
"version": [1, 2, 0]
}
]
}How to increment version arrays
In `format_version` 2 manifests, versions for your pack, modules, and pack dependencies are always written as a JSON array of three non-negative integers: `[major, minor, patch]`. You cannot use strings like `"1.2.0"` for your own pack version (the one exception is when declaring a dependency on a built-in Script API module like `@minecraft/server`, which uses a `module_name` and a string version such as `"1.11.0"`).
When comparing two versions, Minecraft reads from left to right. `[1, 2, 0]` is higher than `[1, 1, 9]`, and `[2, 0, 0]` is higher than `[1, 99, 99]`. It is good practice to follow standard semantic versioning habits so players and server owners understand the scope of your update:
Bump the third number (`patch`, e.g., `[1, 2, 0]` to `[1, 2, 1]`) when you fix a bug in a loot table, correct a typo in a `.lang` file, or tweak a texture without changing how the add-on plays. Bump the middle number (`minor`, e.g., `[1, 2, 1]` to `[1, 3, 0]`) when you add new custom blocks, items, or entities that are safe to drop into an existing world. When you bump the minor number, always reset the patch number to `0`. Bump the first number (`major`, e.g., `[1, 3, 0]` to `[2, 0, 0]`) when you make major architectural changes or remove content that might leave unknown blocks or missing entities in old saves.
Remember that your `dependencies` array enforces a strict version lock. If your Behavior Pack (`[1, 2, 0]`) lists the Resource Pack UUID in its `dependencies` with version `[1, 2, 0]`, the Resource Pack must also have `[1, 2, 0]` in its `header.version`. Here is the partner Resource Pack manifest that pairs with the Behavior Pack shown above:
{
"format_version": 2,
"header": {
"name": "pack.name",
"description": "pack.description",
"uuid": "9f8e7d6c-5b4a-4f3e-8d2c-1b0a9f8e7d6c",
"version": [1, 2, 0],
"min_engine_version": [1, 20, 80]
},
"modules": [
{
"type": "resources",
"uuid": "f0e1d2c3-b4a5-4968-8796-a5b4c3d2e1f0",
"version": [1, 2, 0]
}
],
"dependencies": [
{
"uuid": "5c8a7912-34f1-4a9b-8e11-90123456789a",
"version": [1, 2, 0]
}
]
}How worlds handle updated packs
Understanding how a player's world save actually links to your pack will save you hours of troubleshooting support requests. Inside every Bedrock world folder, there are two JSON files that track active add-ons: `world_behavior_packs.json` and `world_resource_packs.json`. When a player activates your add-on on a world through the game UI, Minecraft writes an entry containing your `pack_id` (your `header.uuid`) and `version` into those files.
When the player imports a newer `.mcaddon` file into Minecraft and opens that existing world, the game reads `world_behavior_packs.json`, checks the global `behavior_packs` folder for all installed packs matching that `pack_id`, and automatically loads the highest version it finds. The player does not even have to open the world settings menu; the upgrade is seamless.
However, this seamless upgrade fails in two common situations. First, if the creator packaged the add-on inside a `.mcworld` template or copied the pack folders directly into the world's local `behavior_packs` and `resource_packs` directories, the world will prioritize its local folder over the global cache. Importing a new `.mcaddon` to the global cache will not overwrite a pack that lives directly inside a world folder. Second, on Bedrock Dedicated Servers (BDS) or manually configured worlds, a server admin might have edited `world_behavior_packs.json` directly. If the version array in that file is higher than what is installed, or if multiple versions of the pack are sitting in the server's `behavior_packs` folder and `world_behavior_packs.json` still lists the old version number, the server may keep loading the old version until the admin updates the version array in `world_behavior_packs.json`.
[
{
"pack_id": "5c8a7912-34f1-4a9b-8e11-90123456789a",
"version": [1, 2, 0]
}
]File-level format_version and min_engine_version
While `header.version` controls whether your pack update reaches the player, two other version numbers control whether your content still works once it gets there: `min_engine_version` in the manifest, and `format_version` at the top of individual JSON files (like blocks, items, and entities).
In `manifest.json`, `min_engine_version` does two things. First, it stops players on older versions of Minecraft from installing a pack their game client cannot run. Second, it tells the Bedrock engine which historical compatibility rules to apply to your entire pack. Mojang regularly changes how vanilla features, Molang queries, or JSON schemas behave, gating the new behavior behind a `min_engine_version` check so older packs do not break. Consequently, you should not blindly bump `min_engine_version` to the latest Minecraft release every time you publish a bugfix for your add-on. Only raise `min_engine_version` when you actually need a feature or fix introduced in that newer game version, and be prepared to test your whole pack when you do.
Similarly, individual files in `BP/blocks/`, `BP/items/`, and `BP/entities/` have their own `format_version` string (such as `"1.20.80"`). This tells Minecraft which schema parser to use for that single file. When you update your add-on, you can create a new block using a modern `format_version` while leaving your older, working blocks on an earlier `format_version` until you have a reason to rewrite them.
Safe habits for data compatibility across updates
Getting the new pack to load is only half the job; your update also has to survive loading into a world that already contains blocks, items, and entities from your previous version. Keep these rules in mind before incrementing your version and shipping:
Never rename custom identifiers (`identifier` in `description`). If you change `my_pack:copper_golem` to `my_pack:golem_copper` in version `1.1.0`, every copper golem already spawned in a player's world will vanish on load, and any custom blocks whose identifier changed will turn into ugly `info_update` dirt blocks.
Be careful when changing block states (`states` inside `description` of a block JSON). If an existing world has placed blocks using a state value that you delete in a pack update, the game will fail to resolve that block permutation. Adding new custom blocks or items is completely safe, removing a state or changing a state's type from integer to boolean on an already-placed block is not.
Finally, when developing in your `development_behavior_packs` and `development_resource_packs` folders, remember that Minecraft caches worlds differently for development folders. Always build your `.mcaddon` file and test the upgrade path like a real player: install version `1.0.0`, create a world, place your custom blocks and spawn your entities, save and quit, then import your `1.1.0` `.mcaddon` and reopen the world.
Which versions this applies to
Applies to all modern Bedrock versions using manifest format_version 2 (Minecraft 1.13.0 through 1.21+). No experimental toggles are required.
When it does not work
| What you see | Why | The fix |
|---|---|---|
| Import fails with the toast message: "Failed to import '[Pack Name]'" and "Duplicate pack detected". | You exported a new `.mcpack` or `.mcaddon` with the exact same `header.uuid` AND the exact same `header.version` array as a pack already installed in your Minecraft storage. | Increment the `header.version` (and `modules[].version`) in your `manifest.json` before packaging, or delete the old version of the pack in Settings > Storage before re-importing. |
| The pack imports fine, but when activating it on a world, Minecraft warns about a missing dependency even though both the BP and RP are installed. | You incremented `header.version` in the Resource Pack (for example to `[1, 2, 0]`), forgot to update the `dependencies[].version` array inside the Behavior Pack (leaving it at `[1, 1, 0]`), and deleted the `1.1.0` Resource Pack. | Ensure `header.version`, `modules[].version`, and `dependencies[].version` are all updated to the exact same `[major, minor, patch]` array across both the BP and RP manifests. |
| After importing the update, the player's world still has the old textures and behaviors, or both the old and new versions show up separately in the pack list. | Either you generated a new `header.uuid` for the update (causing two separate packs in the list), or the old pack was embedded directly inside the world's local `behavior_packs` / `resource_packs` folder, which overrides the global cache. | Revert `header.uuid` to match your original release so Minecraft recognizes it as an upgrade. If the world has local pack folders inside the save directory, remove them or update them directly so the world can read from the global pack cache. |
| Content Log shows an error on startup that `min_engine_version` is missing or invalid, or features stop working as expected after an update. | In `format_version` 2 manifests, `min_engine_version` is mandatory in the `header` of both Behavior and Resource Packs. Alternatively, it was set higher than the player's current Minecraft client version. | Include `"min_engine_version": [1, 20, 80]` (or the earliest version of Minecraft your pack supports) inside the `header` object of both `BP/manifest.json` and `RP/manifest.json`. |
Reference, straight from the game files
Read out of the game itself, so the ids match the version you are building for.
