Skip to content

Why an Action Did Not Emit

Hengo never emits half an action. When something is wrong it drops the action entirely and leaves a loud trace in both places you might look:

# hengo: action set_value (a12) unresolved: input "Target" must be bound to a variable or property

The same text is pushed as a warning to Godot’s Output panel. You also see it without leaving Hengo: the Actions counter in the top bar goes up, clicking it lists the reasons, and the card that caused each one is drawn red on the canvas.

Search the generated file for # hengo: after a compile, an empty phase method with a marker in it is always this.

Marker textWhat happenedFix
macro not foundThe saved action points at an id no file declares any more.Restore the file, or keep get_id() stable when refactoring.
could not instance macroThe file loaded but is not a HenScriptMacroBase.Check @tool and the extends. A parse error in the action file also lands here.
your own textget_validation_error() returned a reason.Whatever your action decided it needs.
<name> can only be used inside a loopAn action with get_needs_loop() sits at the top level.Move it inside a Repeat or For Each.
<name> can only be used inside a functionAn action with get_needs_function(), such as Finish, sits in a state.Move it into a function body.
this finish belongs to another functionA Finish for one function ended up in a different one.Use the Finish of the function you are in.
a function cannot change the state of its own scriptA branch inside a function targets a state of the same script.Report a way out instead, and let the caller decide.
has no <phase> bodyThe action is on a phase it never declared a get_flow_<phase>() for.Change the action’s phase, or declare that body. The message uses the internal phase id, so update is the Every Frame cell.
input "X" must be bound to a variable or propertyAn lvalue or bind_only slot has no usable source. An expression or an inline action never satisfies one.Bind the slot to a variable, a property or a node.
input "X" binds a variable that no longer existsThe bound variable was deleted or renamed away.Rebind the slot. Undoing the delete restores the binding, the link is by id.
expression word "w" binds a variable that no longer existsSame, for a word inside an expression slot.Rebind the word.
input "X" reads a step that no longer existsA wire pointed at a step that was deleted.Re-drag the wire, or give the slot another source.
input "X" inline action is emptyAn inline slot lost its nested action.Re-attach the producer.
input "X" inline action could not be resolvedThe nested action’s macro is missing.Same causes as macro not found, one level down.
input "X" inline action is not a pure value producerThe nested action branches, loops or keeps state.Store its result in a variable and bind the slot to that variable.
no output storedA producer’s outputs are all unbound and nothing wires them, so the body has nothing left to run.Store at least one output, wire it into a step, or remove the action.
a branching action cannot run on exitA transition on the End cell would re-enter the state forever. Steps alone are fine there.Move the transition to Start, Every Frame or Physics.
no branch target setAn action with required branches points nowhere and runs nothing.Set a target, or hang steps on a branch. A branch declared optional never triggers this.
branch "X": missing target instance connectionA cross-script branch has no instance to drive.Give the branch an instance, a bound variable or a node path.
branch "X" points at a sub-state of another stateA sub-state is only reachable from its own parent chain.Target a top-level state, a sibling, or a sub-state of the running chain.

The action does not show up in the search. Hengo scans res://hengo/macros/ when a collection loads, so reopen it. Also check @tool, and check get_target_classes(): an action gated to Node2D is invisible on a Control script.

Two copies of the action collide. Any name your body emits must carry {{VCNODE_ID}}, including local variables, class members and callbacks. Without it the second copy redeclares the first.

A body using delta fails to compile. delta only exists in update and physics. Declare only those flow inputs when the body needs it.

{{input}} came out literally in the code. get_script_base(), get_script_scope() and get_function_overrides() do not run the input substitution. Read the value with value_of(&'id', fallback) and paste it into the string.

A string input emitted quotes you did not want. That is the default: values are emitted as literals. Set raw = true on the slot to emit the text verbatim, which is what pickers built with options need.

A counter keeps its value when the state is re-entered. State objects are built once. Zero it in get_flow_reset(), which runs at the top of enter().

A signal stays connected after leaving the state. Disconnect in get_flow_teardown(), and resolve the emitter once in get_flow_reset() instead of calling get_node() again on the way out.

The generated .gd under res://hengo/scripts/ is the source of truth for what an action emitted. When a body looks right but behaves wrong, read the emitted lines before touching the template.

Back to Write a Custom Action, or the method-by-method Action API.