Wiki 9Minecraft Wiki 9Minecraft

Molang vs. Java Predicates: Conditional Logic in Bedrock

How to translate Java Edition's JSON predicates and NBT checks into Bedrock's inline Molang expression language.

6Sections
5Code examples
4Known errors

When transitioning from Java Edition data packs to Bedrock Edition add-ons, one of the largest architectural shifts is how the game evaluates conditions. Instead of writing nested JSON predicate files or inspecting raw NBT data inside command selectors, Bedrock uses Molang—a fast, inline C-style expression language embedded directly inside entity, block, item, and animation files. Understanding how to map your mental model of predicates onto Molang queries and entity properties is essential for controlling AI behaviors, visuals, and state transitions.

Architectural Shift: JSON Trees vs. Inline Expressions

In Java Edition data packs, conditional logic is heavily structured around JSON trees. If you want to check whether a player is sneaking, holding a specific item, and standing in rain, you build a predicate file in your data pack using conditions like entity_properties, location_check, and inverted terms. Alternatively, you inline those checks into an execute if data or execute if predicate command. The evaluation happens on the server tick, often triggered by a tick function or an advancement criteria.

Bedrock Edition does not have a dedicated predicate folder, nor does it expose raw entity NBT to commands or JSON files. Instead, Bedrock embeds logic directly into the fields where decisions are made using Molang (Minecraft Object Language). Molang is a lightweight scripting language designed originally for animations and particles, which has since expanded to drive server-side entity behaviors, block permutations, item rendering, and dialogue gates. Rather than referencing an external file that returns true or false, you write a mathematical or boolean string directly inside the JSON property you want to control.

Another fundamental difference is the execution context. Java predicates are strictly server-side data pack constructs (aside from simple model overrides in resource packs). In Bedrock, Molang runs on both the client (inside Resource Packs for animations, render controllers, and particles) and the server (inside Behavior Packs for entity filters, environment sensors, and block states). While the syntax is identical in both places, certain queries are restricted depending on whether the client knows about the underlying data.

Java Edition ConceptBedrock EquivalentWhere It Lives in Bedrock
Predicate JSON file (condition: entity_properties)Molang Query (q.is_sneaking, q.health) or Entity FilterInline inside BP entities, RP animations, or block JSON
NBT check (nbt={IsBaby:1b})Query function (q.is_baby) or Filter (is_baby)Entity behavior components, animation controllers
Scoreboard value check in predicateEntity Property (q.property('ns:name'))Defined in BP entity description, read in BP and RP
Item Model Predicate (custom_model_data)Render Controller / Animation Controller MolangRP/render_controllers/ or RP/animation_controllers/
Block state check in location predicateq.block_state('ns:state_name')BP block permutations and RP block geometry visibility

Molang Syntax and Core Concepts for Java Creators

Molang syntax is much closer to JavaScript or C than it is to JSON. Expressions evaluate to a single floating-point number. For boolean logic, a return value of 0.0 represents false, and any non-zero value (typically 1.0) represents true. You can use standard comparison operators (==, !=, <, >, <=, >=), logical operators (&& for AND, || for OR, ! for NOT), and the ternary conditional operator (condition ? true_value : false_value). This means a massive nested Java alternative predicate with multiple inverted blocks collapses into a single line of Molang.

Data in Molang is accessed through specific prefixes, which have short aliases used by almost all experienced developers. Queries (query. or q.) read game state, such as entity health, current dimension, time of day, or equipped items. Variables (variable. or v.) store read-write values attached to that specific entity or particle instance for the duration of a session or tick. Temp variables (temp. or t.) hold values only for the duration of a single expression evaluation, cleaning up immediately afterward. Contexts (context. or c.) provide read-only data passed down by a specific game event, such as the item currently being held in a render controller.

For simple expressions—which make up the vast majority of predicate replacements—you write a single line without a semicolon, and its result is implicitly returned. If you need to calculate intermediate values using temp variables or run multiple conditional branches, you wrap the expression in curly braces, end each statement with a semicolon, and explicitly use the return keyword at the end. Strings inside Molang queries are always wrapped in single quotes, because the Molang expression itself lives inside a JSON double-quoted string.

RP/animation_controllers/example.animation_controllers.json
// Simple implicit return (checks if sneaking and below half health)
q.is_sneaking && (q.health / q.max_health) < 0.5

// Complex multi-statement block using temp variables and explicit return
{
  t.speed = q.ground_speed;
  t.is_moving_fast = t.speed > 4.5;
  return t.is_moving_fast && !q.is_in_water ? 1.0 : 0.0;
}

Translating Entity State Checks: Queries vs. Entity Filters

When you build a Behavior Pack entity in Bedrock, you will actually encounter two different systems for checking conditions: JSON Entity Filters and Molang Queries. This frequently confuses developers coming from Java. Entity Filters are JSON objects that look very similar to Java predicates—they use keys like test, subject, operator, and value, and can be grouped inside all_of, any_of, and none_of arrays. Historically, Bedrock used Entity Filters for all server-side AI components, while Molang was strictly for client-side Resource Packs.

In modern Bedrock versions, these two systems overlap through a specific filter test called has_property, as well as the ability to run Molang directly inside certain behavior components and animation controllers. However, many core AI components—such as minecraft:environment_sensor, minecraft:Interact, or minecraft:damage_sensor—still require a standard JSON filter block as their entry point. If you need to trigger an entity event when an entity enters water while holding a specific item, you can either build an all_of filter tree, or you can attach a server-side Animation Controller to the entity in the Behavior Pack and use pure Molang to fire the event or run slash commands.

Compare how you would check if an entity is currently on fire, not in water, and has a custom state active. In a Behavior Pack component that expects a filter, you write the structured filter object. In a Behavior Pack animation controller (which runs on the server and can execute commands and trigger component groups just like a Java tick function), you write a concise Molang query.

BP/animation_controllers/combat_state.bp_ac.json
{
  "format_version": "1.10.0",
  "animation_controllers": {
    "controller.animation.custom_mob.combat_state": {
      "initial_state": "default",
      "states": {
        "default": {
          "transitions": [
            {
              "enraged": "q.is_on_fire && !q.is_in_water && q.health < (q.max_health * 0.5)"
            }
          ]
        },
        "enraged": {
          "on_entry": [
            "/effect @s speed 10 1 true",
            "/playsound mob.ravager.roar @a[r=16]"
          ],
          "transitions": [
            {
              "default": "!q.is_on_fire || q.is_in_water"
            }
          ]
        }
      }
    }
  }
}

Replacing NBT and Scoreboard Predicates with Entity Properties

In Java Edition, if you want to track a custom state on an entity—such as which phase a boss is in, or whether a villager has been spoken to—you usually add a scoreboard tag, set a scoreboard objective score, or merge custom NBT into the entity's Tags or data storage, then check it with a predicate. In Bedrock, scoreboards cannot be easily read by Resource Packs, and arbitrary NBT access does not exist. The modern, stable replacement for this workflow is Entity Properties.

Entity Properties are strongly typed variables (booleans, integers, floats, or string enums) declared inside the description block of a Behavior Pack entity file. Once declared, they persist with the entity across world saves just like NBT. If you set client_sync to true on a property, the server automatically synchronizes its value to the client whenever it changes. This is one of Bedrock's strongest features: you can change a property via a server-side event or command, and immediately read it in both server filters and client-side visual animations using q.property('namespace:property_name').

Below is a complete snippet of an entity description defining a custom integer property and a string enum property, followed by an environment sensor that modifies the property using an event when the entity is underwater. This completely replaces the Java pattern of running a repeating tick function that checks an execute if predicate condition to modify scoreboard values.

BP/entities/custom_guardian.json
{
  "format_version": "1.20.80",
  "minecraft:entity": {
    "description": {
      "identifier": "wiki:custom_guardian",
      "is_spawnable": true,
      "is_summonable": true,
      "properties": {
        "wiki:charge_level": {
          "type": "int",
          "range": [0, 10],
          "default": 0,
          "client_sync": true
        },
        "wiki:stance": {
          "type": "enum",
          "values": ["idle", "defensive", "aggressive"],
          "default": "idle",
          "client_sync": true
        }
      }
    },
    "components": {
      "minecraft:health": {
        "value": 40,
        "max": 40
      },
      "minecraft:environment_sensor": {
        "triggers": [
          {
            "filters": {
              "all_of": [
                {
                  "test": "in_water",
                  "subject": "self",
                  "value": true
                },
                {
                  "test": "enum_property",
                  "domain": "wiki:stance",
                  "value": "idle"
                }
              ]
            },
            "event": "wiki:enter_defensive_stance"
          }
        ]
      }
    },
    "events": {
      "wiki:enter_defensive_stance": {
        "set_property": {
          "wiki:stance": "defensive",
          "wiki:charge_level": "q.property('wiki:charge_level') + 1"
        }
      }
    }
  }
}

Client-Side Predicates: Render Controllers and Model Overrides

Java resource packs use a predicates system inside item model JSON files to swap textures or geometry based on properties like custom_model_data, damage, pulling, or broken. For entities, vanilla Java does not natively support conditional geometry or texture swapping without third-party mods or riding invisible armor stands with custom item models.

Bedrock handles conditional visuals natively through Render Controllers and bone visibility in Geometry/Animation files, powered entirely by Molang. A Render Controller sits between your entity's resource definition (where you give short names to textures, materials, and geometries) and the rendering engine. Inside the Render Controller, you can use Molang ternaries or array lookups to dynamically pick which texture, model, or material to render on every frame based on the entity's state or synced properties.

Furthermore, within the part_visibility section of a Render Controller, you can toggle individual bones of a 3D model on and off using boolean Molang expressions. If a boss entity's wiki:stance property changes to aggressive on the server, the client's Render Controller immediately sees q.property('wiki:stance') == 'aggressive' evaluate to true, allowing it to swap the entity's skin to an angry texture and unhide armor spikes on its model without a single command or entity swap.

RP/render_controllers/custom_guardian.render_controllers.json
{
  "format_version": "1.8.0",
  "render_controllers": {
    "controller.render.custom_guardian": {
      "geometry": "Geometry.default",
      "materials": [
        {
          "*": "Material.default"
        }
      ],
      "textures": [
        "q.property('wiki:stance') == 'aggressive' ? Texture.aggressive : Texture.default"
      ],
      "part_visibility": [
        {
          "*": true
        },
        {
          "spikes_bone": "q.property('wiki:charge_level') >= 5"
        }
      ]
    }
  }
}

Block States and Permutations

In Java Edition, block states control models via the blockstates JSON file, while server-side behavior based on block states requires checking location predicates or using execute if block commands. In Bedrock, custom blocks combine visual and behavioral state variations into a single file inside the Behavior Pack using the permutations array and Molang.

Each permutation entry in a Bedrock block file has a condition field that takes a Molang string. Here, you use q.block_state('namespace:state_name') to read the block's current state values. When the Molang condition evaluates to true, the components listed inside that permutation override the base components of the block. You can change collision boxes, light emission, friction, loot tables, or geometry visibility dynamically without writing external predicate files.

BP/blocks/charge_lamp.json
{
  "format_version": "1.20.80",
  "minecraft:block": {
    "description": {
      "identifier": "wiki:charge_lamp",
      "states": {
        "wiki:lit": [false, true],
        "wiki:power_level": [0, 1, 2, 3]
      }
    },
    "components": {
      "minecraft:destructible_by_mining": {
        "seconds_to_destroy": 2.0
      },
      "minecraft:light_emission": 0
    },
    "permutations": [
      {
        "condition": "q.block_state('wiki:lit') && q.block_state('wiki:power_level') >= 2",
        "components": {
          "minecraft:light_emission": 15
        }
      },
      {
        "condition": "q.block_state('wiki:lit') && q.block_state('wiki:power_level') == 1",
        "components": {
          "minecraft:light_emission": 7
        }
      }
    ]
  }
}

Which versions this applies to

Core Molang expressions, queries, and Render Controllers apply to all modern Bedrock versions (format_version 1.8.0 and 1.10.0+ for RP files). Entity Properties (replacing the older experimental properties system) and q.block_state are fully stable without any experimental toggles in Bedrock 1.20.80 and newer.

When it does not work

What you seeWhyThe fix
Content Log Error: [Molang][error]-Error: unhandled request for unknown variable 'q.property' or property always returns 0.0 in the Resource Pack.You declared the Entity Property in the Behavior Pack entity file, but either forgot to set "client_sync": true in its definition, or misspelled the namespace:identifier string inside the Resource Pack query.Open the BP entity JSON, locate the property under description.properties, and add "client_sync": true. Ensure the string inside q.property('namespace:name') matches the exact identifier.
JSON parse error or Content Log warning: Syntax error in Molang expression near Double Quote.You used double quotes inside a Molang string argument (for example, "q.block_state("wiki:lit")") without escaping them, or used escaped double quotes where the Molang parser strictly expects single quotes.Always use single quotes (' ') for string literals inside Molang expressions, such as "q.block_state('wiki:lit')" or "q.is_item_name_any('slot.weapon.mainhand', 0, 'minecraft:diamond_sword')".
Complex multi-line Molang expression with semicolons always evaluates to 0.0 (false), preventing animations or transitions from firing.Once you include a semicolon (;) or curly braces ({ }) in a Molang expression, it becomes a complex statement block. Complex blocks do not implicitly return the last evaluated math expression; they default to returning 0.0 unless instructed otherwise.Add the explicit return keyword before the final value in your expression block, for example: "{ t.val = q.health * 2; return t.val > 10; }".
Entity AI component (such as minecraft:environment_sensor) fails to load when putting a Molang string directly into the "filters" field.Behavior Pack AI component filter fields expect a structured JSON Entity Filter object (containing "test", "subject", "value"), not a raw Molang string.Use standard JSON filter syntax inside component "filters" blocks (such as "test": "enum_property" or "test": "int_property"), or move your raw Molang transition logic into a Behavior Pack Animation Controller.

Reference, straight from the game files

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