Skip to main content

Handles

Current ActionFlow handle categories, canvas connection checks, data mappings, and runtime behavior without static type-safety claims.

Handles

A handle is a connection point stored on the canvas graph. Handles can order nodes, carry a named data mapping, select a control lane, or mark a Loop body. They are not TypeScript-style parameters, and the current canvas does not statically type-check every edge or object value.

A valid-looking connection is design-time feedback, not proof that the downstream node will receive a usable value. Inspect the resolved input and the node's runtime result.

Handle categories

CategorySaved handleRuntime meaning
Default/untargeted executionnode-source or no source handle to node-target or no target handleContinue to the target after the source completes. When the saved edge has no targetHandle, the source result is also exposed to the target as inputs[sourceNode.id]; it does not select a named target input.
Named data edgeA named output sourceHandle and matching input targetHandleRead the selected source output path and assign it to that target input key.
Inspector output mappingAn output chip stored as {{nodeId.outputPath}}Resolve the reference recursively in the saved inspector value.
If or Switch lanetrueBranch, falseBranch, or a generated Switch case_* sourceFollow only the lane selected by the control node.
Loop bodyloop-body-in and loop-body-outMark the body executed sequentially for each Loop item.

The current normal node target is the visible node-target execution handle. The inspector's output-chip workflow is the primary way to map an upstream value into a named field. The saved graph and runner also recognize named data edges for flows that contain them.

Output handles and catalog metadata

A node can render one handle for each catalog output. Each output has a name, label, dataType, and optional multiplicity metadata. The output popover and output chips use this metadata to help you select a value.

A catalog output is not a runtime guarantee:

  • a node function can return a different shape;
  • a field can be absent or undefined;
  • an external provider can omit optional metadata; and
  • a function can return success: false with an error.

Use the actual node result and the documented output path. Do not infer a value from a label or output badge alone.

What the canvas validates

useIsValidConnection applies these current connection checks:

  1. Both nodes must exist, and the connection cannot be a self-connection.
  2. A start node cannot be a target.
  3. Occupancy normally rejects reuse of an occupied equivalent source or target handle. For source handles, null, undefined, and node-source are equivalent. For target handles, null, undefined, node-target, and loop-target are equivalent. A shared loop-body-out join into an If or Switch is the current target-side exception: its target occupancy can be shared, while source occupancy is still checked.
  4. Control-body lane and join handles cannot be connected in arbitrary combinations.
  5. Loop-body nodes must stay in a compatible Loop scope and cannot create an invalid external join.
  6. Internal control-body link handles are not user-connectable.

After those structural checks, the canvas does not compare the source and target catalog dataType values. A connection can therefore exist even when the runtime value is unusable for the target function.

The inspector can apply a separate guard when you drag an output chip into a field. That guard checks the declared output type, input type, multiple-value flag, and the allowed upstream node list. It is still editor feedback:

  • known incompatible type combinations can be rejected;
  • unknown or unmapped types can be accepted;
  • object fields are not proven to match at runtime; and
  • a saved or API-supplied value can differ from the catalog metadata.

Use this distinction when debugging: a successful drag does not replace runtime validation.

How data mapping is assembled

At node execution, the runtime assembles inputs in this order:

  1. Start with values saved in inputData.inputs.
  2. Apply connected data edges. A defined mapped value, including null, replaces the saved value with the same target input key. An undefined mapping leaves the saved value in place.
  3. For incoming edges without a targetHandle, execute each reachable source and store its result under inputs[sourceNode.id].
  4. Add the nearest Loop context for a Loop body: item, index, isFirst, and isLast.
  5. Resolve {{nodeId.outputPath}} placeholders in strings, arrays, and objects.
  6. Call the node function.

An untargeted default edge both orders the target and exposes the source result under its source-node ID. It does not populate a named targetHandle input or satisfy a required input whose definition uses a named target handle.

Nested output paths

The resolver first reads the requested dotted path directly. If that path is not present, it tries the same path under the common data wrapper. This supports paths such as {{nodeId.data.text}} as well as the shorter {{nodeId.text}} when the output shape permits it.

An exact placeholder preserves a raw object, array, or buffer. A placeholder embedded in a larger string is converted to text; objects are JSON-stringified and nullish values become an empty string.

Execution order and branches

The current runner has no parallel branch scheduler. Eligible outgoing edges are visited sequentially and depth-first. A target and its descendants are processed before the next eligible edge from the ancestor is visited. A node that has already executed is not rerun merely because another edge points to it.

Control nodes narrow that traversal:

  • If follows the selected trueBranch or falseBranch lane.
  • Switch follows the matched case or configured default lane.
  • Loop runs its body sequentially for every item and injects the current item context.
  • Merge transforms the branches value it receives. It is not a scheduler and does not wait for independent branches.

Do not design around a Merge node as a synchronization barrier or around independent branches running at the same time.

Failure and side effects

A named data edge or placeholder can cause the source node to execute while the target is being prepared. If the source node has external side effects, that can happen earlier than the visible traversal order suggests. A placeholder that refers to a missing, unreachable, or circular node produces an empty or undefined value rather than a design-time type error.

When a node function throws, the normal path stops. When it returns success: false, the output is stored first, but the run fails unless the node is configured with continueOnError: true. A downstream node is not guaranteed to run after either failure.

Debugging a connection

  1. Confirm the source and target node IDs.
  2. Inspect sourceHandle and targetHandle in the saved graph.
  3. Check whether the source output path exists in the actual result.
  4. Check whether a default execution edge is being mistaken for a named input mapping.
  5. Inspect the target's resolved input in live output.
  6. If the path is empty, check reachability, execution order, and whether an earlier node failed.

On this page