Overview
The early return pattern is not a device — it is a Verse coding technique. Instead of nesting all your conditions inside one deep if block, you check each precondition at the top of a function and return (or return a value) the moment one fails. Everything after that check is guaranteed to run only when the condition passed.
In UEFN Verse this matters because:
agentvalues are often wrapped in?agent(option types) and must be unwrapped before use.- Many API calls are failable (they use
<decides>) and must appear insideifexpressions. - Game events fire for every player — you frequently need to filter by team, health, zone, or inventory before acting.
When to reach for it: any handler that has two or more guard conditions before the real work begins.
API Reference
(API surface could not be resolved for this device.)
Walkthrough
Scenario — The Clifftop Dock Reward
Sunny cove, 2D cel-shaded. A pressure plate sits on the clifftop dock. When a player steps on it the lighthouse keeper checks:
- Was a real agent passed? (unwrap
?agent) - Is the agent actually a
fort_character? (failable cast) - Is the character still alive? (
IsActive[]— failable)
Only if all three pass does the keeper grant an item and enable a barrier device that opens the sea-gate.
using { /Fortnite.com/Devices }
using { /Fortnite.com/Characters }
using { /Verse.org/Simulation }
# Place a trigger_device (the dock pressure plate),
# an item_granter_device (the keeper's reward),
# and a barrier_device (the sea-gate) in your UEFN level
# and wire them to these @editable fields.
dock_reward_manager := class(creative_device):
@editable
DockPlate : trigger_device = trigger_device{}
@editable
RewardGranter : item_granter_device = item_granter_device{}
@editable
SeaGate : barrier_device = barrier_device{}
# Entry point — subscribe to the plate's event.
OnBegin<override>()<suspends> : void =
# Sea-gate starts closed.
SeaGate.Enable()
# Listen for any player stepping on the dock plate.
DockPlate.TriggeredEvent.Subscribe(OnDockTriggered)
# Called every time the plate fires.
# The event passes ?agent, so we must unwrap it.
OnDockTriggered(MaybeAgent : ?agent) : void =
# GUARD 1 — was there actually an agent?
# Early return if the option is empty.
if (A := MaybeAgent?):
HandleDockedPlayer(A)
# All real logic lives here, behind clean guards.
HandleDockedPlayer(A : agent) : void =
# GUARD 2 — can we get a fort_character from this agent?
# GetFortCharacter[] is failable (<decides>), so it lives in an if.
if (Character := A.GetFortCharacter[]):
# GUARD 3 — is the character still alive?
# IsActive[] is also failable.
if (Character.IsActive[]):
# Happy path — all guards passed.
# Grant the lighthouse keeper's reward.
RewardGranter.GrantItem(A)
# Open the sea-gate for this player.
SeaGate.Disable()
Line-by-line explanation
| Lines | What's happening |
|---|---|
@editable fields |
Lets you wire the plate, granter, and gate in the UEFN editor without touching code. |
SeaGate.Enable() |
Closes the barrier at game start so the gate is locked. |
DockPlate.TriggeredEvent.Subscribe(OnDockTriggered) |
Registers the handler; the event signature is listenable(?agent). |
OnDockTriggered(MaybeAgent : ?agent) |
Matches the event's parameter type exactly. |
if (A := MaybeAgent?) |
Guard 1 — unwraps the option. If it's false (no agent), the function exits here. |
if (Character := A.GetFortCharacter[]) |
Guard 2 — failable call; exits if the agent has no character. |
if (Character.IsActive[]) |
Guard 3 — exits if the character is already eliminated. |
RewardGranter.GrantItem(A) |
Only reached when every guard passed — the happy path. |
Common patterns
Pattern 1 — Flat guards with early return in a helper function
Instead of nesting three if blocks you can chain guards in one if expression using , (logical AND in Verse). This is the flattest possible form.
using { /Fortnite.com/Devices }
using { /Fortnite.com/Characters }
using { /Verse.org/Simulation }
# A shore-side chest opener: only living players on the correct team
# can open the treasure chest (represented by an item_granter_device).
shore_chest_opener := class(creative_device):
@editable
ChestPlate : trigger_device = trigger_device{}
@editable
TreasureGranter : item_granter_device = item_granter_device{}
OnBegin<override>()<suspends> : void =
ChestPlate.TriggeredEvent.Subscribe(OnChestStep)
OnChestStep(MaybeAgent : ?agent) : void =
# All three guards in one flat if — comma means AND.
# If any guard fails, the whole block is skipped (early return).
if:
A := MaybeAgent? # Guard 1: real agent
Character := A.GetFortCharacter[] # Guard 2: has a character
Character.IsActive[] # Guard 3: still alive
then:
# Happy path — grant the shore treasure.
TreasureGranter.GrantItem(A)
Pattern 2 — Early return from a value-returning helper
Sometimes you want to compute something (like a score bonus) and return early with a default value when conditions aren't met. Verse functions can return values, and you can use if/else as an expression.
using { /Fortnite.com/Devices }
using { /Fortnite.com/Characters }
using { /Verse.org/Simulation }
# Clifftop bonus calculator: awards extra score for surviving players.
# Returns 0 if the player is eliminated, otherwise returns 100.
clifftop_bonus_device := class(creative_device):
@editable
BonusPlate : trigger_device = trigger_device{}
@editable
ScoreGranter : score_manager_device = score_manager_device{}
OnBegin<override>()<suspends> : void =
BonusPlate.TriggeredEvent.Subscribe(OnBonusStep)
# Helper that returns a bonus amount — 0 is the "early return" default.
GetBonus(A : agent) : int =
# If the character is not active, the if fails and we fall to else.
if (Character := A.GetFortCharacter[], Character.IsActive[]):
100 # Alive — full clifftop bonus
else:
0 # Eliminated — no bonus (early-return equivalent)
OnBonusStep(MaybeAgent : ?agent) : void =
# Guard: unwrap the agent option first.
if (A := MaybeAgent?):
Bonus := GetBonus(A)
# Only call the score manager if there is actually a bonus.
if (Bonus > 0):
ScoreGranter.SetScoreForPlayer(A, Bonus)
Pattern 3 — Guarding against repeated triggers with a flag
A common island bug: the reward fires multiple times because the plate can be stepped on again. An early return on a var logic flag prevents double-granting.
using { /Fortnite.com/Devices }
using { /Fortnite.com/Characters }
using { /Verse.org/Simulation }
# Cove beacon: can only be activated once per round.
cove_beacon_device := class(creative_device):
@editable
BeaconPlate : trigger_device = trigger_device{}
@editable
BeaconGranter : item_granter_device = item_granter_device{}
# Flag: has the beacon already been claimed?
var BeaconClaimed : logic = false
OnBegin<override>()<suspends> : void =
BeaconPlate.TriggeredEvent.Subscribe(OnBeaconStep)
OnBeaconStep(MaybeAgent : ?agent) : void =
# GUARD 1 — early return if already claimed.
# `not BeaconClaimed` fails (and exits) if BeaconClaimed is true.
if (not BeaconClaimed):
if (A := MaybeAgent?):
if (Character := A.GetFortCharacter[], Character.IsActive[]):
# Mark claimed BEFORE granting to prevent race conditions.
set BeaconClaimed = true
BeaconGranter.GrantItem(A)
Gotchas
1. ?agent is NOT agent — always unwrap
Events like trigger_device.TriggeredEvent pass ?agent, not agent. If you write OnTriggered(A : agent) the handler signature won't match and the subscription will silently fail or not compile. Always declare (MaybeAgent : ?agent) and unwrap with if (A := MaybeAgent?).
2. Failable calls MUST live inside if
GetFortCharacter[] and IsActive[] use the <decides> effect — they can fail. You cannot call them outside an if expression. Trying to do so is a compile error. This is exactly why the early-return pattern pairs so naturally with Verse: every guard is just an if that exits on failure.
3. if with multiple guards uses , not and
In Verse the comma , inside an if block is the logical AND for failable/boolean expressions. Writing if (A and B) where B is a failable expression will not compile. Use:
if (A := MaybeAgent?, Character := A.GetFortCharacter[], Character.IsActive[]):
4. set keyword required for mutable variables
When updating a var field (like the BeaconClaimed flag in Pattern 3), you must use set BeaconClaimed = true. Writing BeaconClaimed = true is a compile error — Verse treats bare assignment as a new binding, not a mutation.
5. message parameters need a localizes wrapper
If you ever pass text to a device method that expects a message type (e.g. a notification device), you cannot pass a raw string literal. Declare a helper:
MyText<localizes>(S : string) : message = "{S}"
Then call NotificationDevice.Show(MyText("Beacon claimed!")). There is no StringToMessage function in Verse.
6. Early return does not exist as a keyword — use if/else structure
Unlike C++ or Python, Verse has no return statement mid-function. The early-return effect is achieved by putting the happy-path code inside the if block and leaving the else (or simply the end of the function) empty. This is idiomatic Verse and compiles cleanly.