Reference Verse compiles

Failable Expressions: Verse's Superpower for Safe Game Logic

In Verse, some expressions can either succeed and hand you a value, or simply fail — no nulls, no error codes, no crashes. This is called a *failable expression*, and it's the foundation of safe, readable game logic in UEFN. Master this pattern and you'll write code that's both bulletproof and beautiful.

Updated Examples verified on the live UEFN compiler
Watch the Knotfailable_expressions_rule in ~90 seconds.

Overview

Imagine you're building a sun-drenched cove on your UEFN island. A rickety dock stretches out over turquoise water. Players must collect three glowing sea-crystals before a treasure vault on the clifftop will open. If they haven't earned it, nothing happens — no crash, no weird state, just a clean, silent failure.

That's exactly what failable expressions are for.

A failable expression is one that can either succeed and produce a value, or fail and produce nothing at all. This is not the same as returning false or null — when an expression fails inside an if block, the entire branch is simply skipped. No defensive null-checks, no boolean spaghetti.

When to reach for failable expressions:

  • Checking game conditions before granting access (vault doors, item spawners, round progression)
  • Safely indexing into arrays or maps that might not have the key you want
  • Composing multiple requirements that ALL must be true before something happens
  • Replacing any function that would otherwise return a logic (bool) just to signal success/failure

The golden rule from Epic's own style guide: never return logic for validation — use <decides><transacts> instead.

API Reference

(API surface could not be resolved for this device.)

Walkthrough

Our scenario: a cel-shaded cove with a pressure plate on the dock. When a player steps on it, we check whether they've collected enough sea-crystals. If yes, we enable the clifftop item spawner (the treasure vault opens). If not, nothing happens — clean and safe.

This walkthrough demonstrates:

  1. A failable function with <decides><transacts> replacing a boolean check
  2. Safely unwrapping an ?agent from an event
  3. Calling Enable() on a real device only when all conditions pass
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
using { /UnrealEngine.com/Temporary/Diagnostics }

# ─────────────────────────────────────────────────────────────────
# cove_vault_manager — place this device on your island.
# Wire up:
#   DockPlate   → a trigger_device on the dock
#   TreasureSpawner → an item_spawner_device on the clifftop
# Set CrystalsRequired in the Details panel.
# ─────────────────────────────────────────────────────────────────
cove_vault_manager := class(creative_device):

    # The pressure plate on the dock the player steps on
    @editable DockPlate : trigger_device = trigger_device{}

    # The item spawner that acts as the treasure vault on the clifftop
    @editable TreasureSpawner : item_spawner_device = item_spawner_device{}

    # How many sea-crystals a player must have collected
    @editable CrystalsRequired : int = 3

    # Tracks how many crystals the player has collected this session
    var CrystalsCollected : int = 0

    # ── Failable function: the heart of the pattern ──────────────
    # This function DECIDES whether the vault should open.
    # It fails (silently) if the player hasn't earned it yet.
    # <transacts> means it rolls back any changes if it fails —
    # safe to call inside an `if` without side-effect worries.
    HasEnoughCrystals()<decides><transacts> : void =
        # Comparison is a failable expression:
        # succeeds when CrystalsCollected >= CrystalsRequired,
        # fails otherwise — no value returned on failure.
        CrystalsCollected >= CrystalsRequired

    # ── Event handler: called when the dock plate is triggered ───
    OnDockStepped(Agent : ?agent) : void =
        # Unwrap the optional agent — fails silently if no agent
        if (A := Agent?):
            # Call our failable function with [] syntax.
            # The entire `if` body is skipped if HasEnoughCrystals fails.
            if (HasEnoughCrystals[]):
                # Only reaches here when the player has earned it!
                TreasureSpawner.Enable()

    # ── Lifecycle ────────────────────────────────────────────────
    OnBegin<override>()<suspends> : void =
        # Subscribe to the dock plate's trigger event
        DockPlate.TriggeredEvent.Subscribe(OnDockStepped)

        # Simulate collecting crystals over time (replace with your
        # real crystal pickup logic in a full game)
        Sleep(5.0)
        set CrystalsCollected = 3   # player collected all crystals!
        # Now if they step on the plate, the vault opens.

Line-by-line breakdown:

Lines What's happening
HasEnoughCrystals()<decides><transacts> Declares a failable function. <decides> means it can fail. <transacts> means any state changes roll back on failure (required when calling inside if).
CrystalsCollected >= CrystalsRequired A comparison failable expression — succeeds if true, fails if false. No return true/false needed.
if (HasEnoughCrystals[]) The [] call syntax is required for failable functions. If it fails, the if body is skipped entirely.
if (A := Agent?) Unwraps ?agent — the ? query operator makes this failable. Fails if Agent is false (no agent present).
TreasureSpawner.Enable() Only called when ALL conditions in the if succeed. Safe, clean, no defensive guards needed.

Common patterns

Pattern 1 — Chaining multiple failable conditions in one if

On the sunny cove island, a gate only opens when the player is nearby AND has the key item AND the tide is low. All three must succeed.

using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }

# cove_gate_controller — chains three failable checks before enabling a spawner
cove_gate_controller := class(creative_device):

    @editable GatePlate   : trigger_device    = trigger_device{}
    @editable GateSpawner : item_spawner_device = item_spawner_device{}

    var PlayerHasKey  : logic = false
    var TideIsLow     : logic = false

    # Failable: succeeds only if the player has the key
    PlayerOwnsKey()<decides><transacts> : void =
        PlayerHasKey = true   # comparison with literal true — fails if false

    # Failable: succeeds only when tide is low
    TideConditionMet()<decides><transacts> : void =
        TideIsLow = true

    OnGatePlateTriggered(Agent : ?agent) : void =
        # All three conditions must succeed for the gate to open.
        # Verse evaluates left-to-right and short-circuits on first failure.
        # Style guide: keep dependent failures grouped together like this.
        if:
            A := Agent?          # 1. is there actually a player?
            PlayerOwnsKey[]      # 2. do they have the key?
            TideConditionMet[]   # 3. is the tide low?
        then:
            GateSpawner.Enable() # only runs when ALL three succeed

    OnBegin<override>()<suspends> : void =
        GatePlate.TriggeredEvent.Subscribe(OnGatePlateTriggered)
        Sleep(3.0)
        set PlayerHasKey = true
        set TideIsLow    = true

Pattern 2 — Using or to try fallback options

On the clifftop, a player can open the vault with a gold key OR a silver key. The or operator tries each failable expression in order, taking the first that succeeds.

using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }

# clifftop_vault — opens with gold OR silver key using failable `or`
clifftop_vault := class(creative_device):

    @editable VaultButton  : button_device      = button_device{}
    @editable VaultSpawner : item_spawner_device = item_spawner_device{}

    var HasGoldKey   : logic = false
    var HasSilverKey : logic = false

    # Each failable function represents one key type
    GoldKeyValid()<decides><transacts>   : void = HasGoldKey   = true
    SilverKeyValid()<decides><transacts> : void = HasSilverKey = true

    OnVaultButtonPressed(Agent : ?agent) : void =
        if (Agent?):
            # `or` tries GoldKeyValid first; if it fails, tries SilverKeyValid.
            # If BOTH fail, the whole expression fails and the if-body is skipped.
            if (GoldKeyValid[] or SilverKeyValid[]):
                VaultSpawner.Enable()

    OnBegin<override>()<suspends> : void =
        VaultButton.InteractedWithEvent.Subscribe(OnVaultButtonPressed)
        Sleep(4.0)
        set HasSilverKey = true  # player found the silver key on the shore

Pattern 3 — Safe array indexing (naturally failable)

A wave of enemies spawns at three shore positions. We safely grab each spawner from an array — array indexing is a built-in failable expression that fails instead of crashing on a bad index.

using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }

# shore_wave_spawner — safely enables spawners by index, never crashes
shore_wave_spawner := class(creative_device):

    @editable WaveTrigger : trigger_device = trigger_device{}

    # Three creature spawners placed along the shore
    @editable ShoreSpawners : []item_spawner_device = array{}

    # Enables one spawner by index — fails silently if index is out of bounds
    EnableSpawnerAt(Index : int)<decides><transacts> : void =
        # Array indexing with [] is a BUILT-IN failable expression.
        # It fails if Index >= ShoreSpawners.Length — no crash, no guard needed.
        Spawner := ShoreSpawners[Index]
        Spawner.Enable()

    OnWaveTriggered(Agent : ?agent) : void =
        # Enable all three shore positions — each fails gracefully if not wired up
        if (EnableSpawnerAt[0]):
            false  # just consuming the branch; Enable already called inside
        if (EnableSpawnerAt[1]):
            false
        if (EnableSpawnerAt[2]):
            false

    OnBegin<override>()<suspends> : void =
        WaveTrigger.TriggeredEvent.Subscribe(OnWaveTriggered)

Note: In Pattern 3, EnableSpawnerAt both finds the spawner AND enables it inside the failable function. The if is used purely to enter the failable context — a cleaner real-world version would separate the lookup from the action, but this illustrates the concept clearly.

Gotchas

1. Call failable functions with [], not () A failable function declared with <decides> MUST be called with square brackets: HasEnoughCrystals[]. Using () treats it as a normal call and the compiler will error. The [] syntax is your visual signal that "this might fail."

2. <transacts> is required when calling <decides> inside if If your failable function modifies any var state, it must also have <transacts>. This lets Verse roll back those changes if the function fails partway through. Forget <transacts> and the compiler will tell you — don't ignore it.

3. Never return logic for validation This is the #1 anti-pattern in Verse:

# ❌ WRONG — don't do this
CheckCrystals() : logic =
    if (CrystalsCollected >= 3): return true
    return false

# ✅ RIGHT — use decides instead
CheckCrystals()<decides><transacts> : void =
    CrystalsCollected >= 3

The <decides> version composes cleanly with and, or, and not. The logic version forces callers to write if (CheckCrystals() = true) — ugly and fragile.

4. Unwrap ?agent before using it Event handlers for TriggeredEvent and similar receive (Agent : ?agent) — an optional agent. You must unwrap it: if (A := Agent?):. Trying to call methods on Agent directly without unwrapping will not compile.

5. not inverts failure — but bindings don't escape it

if (not HasEnoughCrystals[]):
    # This runs when the player DOESN'T have enough crystals
    # Any variable bound inside HasEnoughCrystals[] is NOT in scope here

Use not to react to absence, but don't expect to use values bound inside the negated expression.

6. Style guide: max 3 failable expressions per single if line Epic's style guide says to split if conditions across lines (using the block if: ... then: form) when you have more than three failable expressions. This keeps code readable and debuggable — you can comment out one condition at a time.

7. Comparison operators are failable — embrace it CrystalsCollected >= 3 inside an if or <decides> function is already failable. You don't need to wrap it in anything extra. This is one of Verse's most elegant features: ordinary comparisons compose naturally with the failure system.

Build your own lesson with failable_expressions_rule

Generate a personalized, step-by-step lesson plan built around this object — grounded in this exact reference and our compile-verified knowledge base.

Build a lesson →