Skip to content
Bit Quirky Behavior Trees and
State Machines

Search documentation

All topics and APIs

Close returns focus to Search.

What are you looking for?

Try a common topic or enter a complete API identifier.

Browse the reference instead

Esc closes · Tab reaches results · Enter opens the focused link

v0.6.0
Menu

Faults

Tell an ordinary Failure, a target that did not resolve, and a real fault apart, and know where each one is recorded.

A guard that walks to the wrong place has a graph problem. A guard that stops deciding entirely has a fault. The runtime keeps those apart, and so should you.

Dungeon Heist’s guards drive their patrol through coroutine members: MoveToCurrentWaypoint, WaitOutWaypointPause, LookAround. Each one starts, reports Running, and finishes later. Three different things can go wrong with that and they look similar from a distance: the move finishes and reports that it could not reach the post, the coin the guard was told to inspect was collected so the target no longer resolves, or the coroutine throws on step four.

Only the third is a fault. The first is ordinary control flow, and the second fails one evaluation and leaves the binding sound.

Result

Success
The node finished the job its parent asked for. A search that proves nobody is there has succeeded at searching.
Failure
The node finished normally without satisfying its parent. A Try method returning false, a comparison that missed, an action reporting it could not complete.
Running
The current activation still owns unfinished work: a cursor, a pending command, a coroutine, a subscription, or movement.

A fourth ordinary reading, Blocked, says a read-only guard is currently false. Control flow treats it as a missed choice while the rail keeps the precise word.

Failure is a design tool. You decide what the graph does with it, which is the whole point of a Selector.

The runtime carries several more results, and a composite propagates them instead of treating them as a missed choice to route around.

Result What it means
Interrupted Active work was cancelled through interruption or lifecycle ownership. A member node’s interrupt result is fixed, not authored.
Timeout A Time Limit or an authored give-up policy ended the wait.
External service refused The composed game service declined the request.
Authority mismatch, authority handoff An execution-ownership boundary inside the behavior artifact.
Execution limit exceeded Runaway zero-time advancement was stopped.
Faulted Configuration or execution broke. Never an authored Failure.
  • A bound member throws on the call.
  • A bound coroutine throws on one of its steps.
  • A deferred start finds its targets unusable.
  • The target object a node held was destroyed, which is recorded as target loss and carries no exception.
  • On the entity path, a target that cannot be resolved for a member operation.

What is not a fault: a blackboard variable that is empty or holds the wrong type when a node evaluates. That fails the one evaluation, keeps the binding valid, leaves the bake verdict untouched, and shows up as the amber TARGET UNRESOLVED THIS EVALUATION reading.

A fault is terminal for the agent. The machine result becomes Faulted, the last result is recorded once, and the agent stops ticking, so the same throwing member is never called again.

  1. Read Last result in the rail’s Graph runtime status. A faulted graph prints Faulted there. That row is the reliable reading.

  2. Read the canvas with that in mind. A faulted result prints IDLE on the card, and the faulting agent clears its active node, so usually no card is marked at all.

  3. Read the agent’s fault trace from your own code or the Inspector: BehaviorAgent.MemberFaultTrace holds up to sixteen records, counting overflow instead of growing.

  4. Read one record. Each carries a stable code, the owner, the node id, the member name, and the exception, and formats as one line: member-threw on 'Guard_2' node 'n_412' member 'LookAround': NullReferenceException: .... The codes are member-threw, member-target-lost, and member-coroutine-faulted.

Two honest limits about where a fault is not recorded. Unity’s console gets no entry for a member fault: the fault record deliberately does not log itself. Live does not display the fault trace either, so Last result plus the trace from code is the route. The rail’s On fault control is authored on the card, and in this release the runtime reports Faulted whatever that control says.

An unresolved target is coalesced into one console record per occurrence, written when the occurrence closes: when the target resolves again, when the reason or held type changes, or when pause, reset, or disposal flushes it. A permanently empty variable never logs once per tick.

The GameObject record names the graph, the node, the variable, what it held, and the reason:

Plain text
gate_watch · open_current: target variable current_door is empty. Gate required.
REASON · EMPTY · OCCURRENCE · 1 · EVALUATIONS · 1

The reason tag is one of REASON · TYPE MISMATCH, REASON · MISSING COMPONENT, or REASON · EMPTY. The entity record has the same shape with the World and the entity in front. Both are ordinary logs rather than warnings, because the world moved and the graph stayed valid.

Cancelling long work is not a fault either

Section titled “Cancelling long work is not a fault either”

Dungeon Heist’s guard gets interrupted constantly: a coin lands and the reactive abort drops the patrol mid-step. That cancellation stops the coroutine once, and its remaining steps never run.

  • The node reports Interrupted, and nothing is stored from the cancelled call.
  • Output values already written when the call started stay written. Cancellation does not roll them back.
  • A late completion from an abandoned activation cannot report anything, because the activation’s generation no longer matches.
  • If an awaited call does not accept cancellation, your code keeps running to its own end outside the graph, and whatever it returns is discarded.
  • A Cancel that itself throws is recorded as a coroutine fault, which is the one way cancellation produces a fault record.

Give-up is separate again: when a give-up policy ends the wait, the node reports the authored give-up result and the outstanding work is released on the same terms as an interruption.

When it goes wrong

SymptomCheckFix
The whole graph went quiet and every card reads IDLE.Read Last result in the rail for Faulted.Read the fault trace, fix the member that threw, and re-enter Play Mode. A faulted agent stays stopped by design.
A node keeps failing and the toolbar still says the graph is valid.Look for the amber target count and read the status bar.Give that variable a usable object. An unresolved target is a world problem, not a graph problem.
The console is silent and something clearly broke.Member faults are recorded in the fault trace instead of the console.Read BehaviorAgent.MemberFaultTrace, or Last result in the rail.
An On fault choice has no effect.This release reports Faulted for every runtime fault.Handle the failure inside your own member and return an ordinary result the graph can route.
Outputs from a cancelled call are still set.Cancellation stops remaining steps and keeps what was already written.Write outputs at the end of the call, or treat them as provisional in your code.
A late result from an old activation appeared to do nothing.Generations guard that path.Nothing to repair. A stale completion cannot update a newer activation.
Full-size image