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:
- A failable function with
<decides><transacts>replacing a boolean check - Safely unwrapping an
?agentfrom an event - 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,
EnableSpawnerAtboth finds the spawner AND enables it inside the failable function. Theifis 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.