Wiki 9Minecraft Wiki 9Minecraft

Avoiding the Block Permutation Limit in Custom Blocks

Techniques to keep custom block states under Bedrock's 65,536 total permutation cap.

5Sections
3Code examples
3Known errors

Minecraft Bedrock Edition enforces a strict engine limit of 65,536 total block permutations across a single world, including all vanilla blocks. When creating complex custom blocks like connected fences, multi-axis logs, or dyed furniture, multiplicative state combinations can quickly exhaust this budget. This reference covers how to calculate your permutation footprint and refactor your blocks to stay well below the cap.

Understanding the Permutation Math

Every custom block you define takes up at least one slot in the global block palette. When you add states or traits to a block, the game pre-generates a unique permutation for every possible combination of those states, regardless of whether you actually write a condition for them in the permutations array. The total number of permutations for a single block is calculated by multiplying the number of values in each of its states together.

For example, if a block has a cardinal direction trait (4 values), a boolean toggle state (2 values), and a color state (16 values), that single block registers 4 × 2 × 16 = 128 permutations. If you also face a per-block limit—where a single block definition cannot exceed a maximum number of states or values—the global limit of 65,536 is still the most common wall large add-ons hit. Once a world crosses 65,536 total permutations across vanilla and all active behavior packs, any additional custom blocks will fail to load.

State SetupMathPermutations UsedImpact
No states11Minimal
Cardinal direction + 4 growth stages4 × 416Safe for most blocks
6-way facing + 16 colors + powered boolean6 × 16 × 2192High; split by color if possible
6 boolean connection states (fences/pipes)2 × 2 × 2 × 2 × 2 × 264Moderate; avoid adding colors or variants to this block

Splitting Variants into Separate Block Files

One of the most common mistakes is using a custom state to handle static material or color variants alongside functional states. While splitting a 16-color block into 16 separate JSON files still uses the exact same number of permutations if their functional states are identical, it becomes a massive saver when only certain variants need extra states, or when you hit the per-block state limit.

More importantly, separating static variants prevents accidental multiplication. If you have a furniture block with 8 wood types and you later decide to add a 'waterlogged' or 'open' state to just the oak variant, keeping them in one file forces all 8 wood types to multiply by that new state. Splitting them into individual block definitions keeps your state math isolated and predictable.

Using Geometry Part Visibility Instead of Permutations

While you still need block states to store data about a block (such as whether a pipe connects north, south, east, or west), you do not need to write a separate entry in the permutations array for every combination. Writing 64 entries in the permutations array bloats your JSON file and increases parsing time.

Instead, define a single geometry file with separate bones for each connection arm, and use the part_visibility field inside the minecraft:geometry component. This allows you to evaluate Molang queries using q.block_state() directly on the model bones. You still use 64 permutations in the engine palette because of the 6 boolean states, but you avoid combining them with redundant visual states.

BP/blocks/cable.json
{
  "format_version": "1.20.80",
  "minecraft:block": {
    "description": {
      "identifier": "example:cable",
      "states": {
        "example:north": [false, true],
        "example:south": [false, true],
        "example:east": [false, true],
        "example:west": [false, true]
      }
    },
    "components": {
      "minecraft:destructible_by_mining": {
        "seconds_to_destroy": 1.0
      },
      "minecraft:geometry": {
        "identifier": "geometry.example.cable",
        "bone_visibility": {
          "arm_north": "q.block_state('example:north')",
          "arm_south": "q.block_state('example:south')",
          "arm_east": "q.block_state('example:east')",
          "arm_west": "q.block_state('example:west')"
        }
      },
      "minecraft:material_instances": {
        "*": {
          "texture": "example_cable",
          "render_method": "alpha_test"
        }
      }
    }
  }
}

Pruning Unused Trait Values

Block traits like minecraft:placement_direction and minecraft:placement_position are convenient ways to add vanilla-style rotation without writing custom scripts. However, traits inject a fixed number of states into your block. For example, using the facing_direction state from minecraft:placement_direction adds 6 values (down, up, north, south, west, east).

If your block only ever rotates horizontally, using facing_direction wastes 2 multipliers (up and down) across every other state on your block. Always use cardinal_direction (4 values) for horizontal-only blocks. Similarly, if a block only needs to flip upside down (like a slab), only enable vertical_half (2 values) in minecraft:placement_position rather than combining multiple positional traits.

BP/blocks/carved_pillar.json
{
  "format_version": "1.20.80",
  "minecraft:block": {
    "description": {
      "identifier": "example:carved_pillar",
      "traits": {
        "minecraft:placement_direction": {
          "enabled_states": ["minecraft:cardinal_direction"]
        }
      }
    },
    "components": {
      "minecraft:geometry": "geometry.example.carved_pillar",
      "minecraft:material_instances": {
        "*": {
          "texture": "example_carved_pillar",
          "render_method": "opaque"
        }
      }
    },
    "permutations": [
      {
        "condition": "q.block_state('minecraft:cardinal_direction') == 'north'",
        "components": {
          "minecraft:transformation": { "rotation": [0, 0, 0] }
        }
      },
      {
        "condition": "q.block_state('minecraft:cardinal_direction') == 'south'",
        "components": {
          "minecraft:transformation": { "rotation": [0, 180, 0] }
        }
      },
      {
        "condition": "q.block_state('minecraft:cardinal_direction') == 'west'",
        "components": {
          "minecraft:transformation": { "rotation": [0, 90, 0] }
        }
      },
      {
        "condition": "q.block_state('minecraft:cardinal_direction') == 'east'",
        "components": {
          "minecraft:transformation": { "rotation": [0, 270, 0] }
        }
      }
    ]
  }
}

Combining Mutually Exclusive States

Beginners often create separate boolean states for conditions that can never happen at the same time. Consider a custom furnace or machine that can be idle, active, or overheated. If you create two boolean states—example:is_active (2 values) and example:is_overheated (2 values)—the game generates 2 × 2 = 4 permutations, even though the block can never be both active and overheated simultaneously.

By refactoring mutually exclusive booleans into a single string or integer state, such as example:machine_status with values 'idle', 'active', and 'overheated', you reduce the multiplier from 4 down to 3. When multiplied against a 4-way cardinal direction trait, that saves 4 permutations on a single block. Applied across dozens of machines in a tech add-on, this technique saves thousands of global permutations.

BP/blocks/generator.json
{
  "format_version": "1.20.80",
  "minecraft:block": {
    "description": {
      "identifier": "example:generator",
      "states": {
        "example:status": ["idle", "active", "overheated"]
      }
    },
    "components": {
      "minecraft:destructible_by_mining": {
        "seconds_to_destroy": 3.0
      },
      "minecraft:material_instances": {
        "*": {
          "texture": "generator_idle",
          "render_method": "opaque"
        }
      }
    },
    "permutations": [
      {
        "condition": "q.block_state('example:status') == 'active'",
        "components": {
          "minecraft:material_instances": {
            "*": {
              "texture": "generator_active",
              "render_method": "opaque"
            }
          },
          "minecraft:light_emission": 12
        }
      },
      {
        "condition": "q.block_state('example:status') == 'overheated'",
        "components": {
          "minecraft:material_instances": {
            "*": {
              "texture": "generator_overheated",
              "render_method": "opaque"
            }
          },
          "minecraft:light_emission": 6
        }
      }
    ]
  }
}

Which versions this applies to

Applies to all modern Bedrock versions using format_version 1.19.40 and above. The bone_visibility feature inside minecraft:geometry requires format_version 1.19.80 or higher. No experimental toggles are required.

When it does not work

What you seeWhyThe fix
Custom blocks at the end of the alphabetical load order do not appear in the creative inventory or fail to place, and the Content Log warns that the maximum number of block permutations has been reached.The combined permutations of vanilla blocks and all loaded behavior packs have exceeded the 65,536 global engine limit.Audit blocks with multiple states, replace facing_direction (6 values) with cardinal_direction (4 values) where possible, and merge mutually exclusive boolean states into single string or integer states.
A single complex block fails to load entirely, throwing a Content Log error stating the block has too many permutations or properties.In addition to the global limit, Bedrock enforces a per-block limit on how many total permutations a single block identifier can generate.Split the block into multiple separate block files (for example, separating a 16-color state into 16 individual block identifiers like example:chair_red, example:chair_blue).
Bones specified in bone_visibility never hide, regardless of the block state.The geometry component is using the older part_visibility syntax under a modern format_version, or the Molang expression is returning a string instead of a boolean.Ensure you are using bone_visibility inside minecraft:geometry for format_version 1.19.80+, and verify that non-boolean states use a comparison operator like q.block_state('example:stage') == 2.

Reference, straight from the game files

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