Skip to main content
Version: 1.0.4

Execution & Triggering

What starts a pipeline

TriggerNodeFires when
ScheduleInjectAn interval elapses or a cron expression matches
StartupInjectThe pipeline starts
Tag changeIndustrial I/O (subscribe)A subscribed tag changes
Alarm eventAlarm (subscribe)An alarm is raised, cleared or acknowledged
HTTP requestREST API (ingress)A matching request arrives
Another pipelineLink (ingress)A linked egress node sends
ManualThe Run button

A pipeline must be running for its triggers to be armed. Manual execution runs the graph once regardless.

Message flow

Node A emits one message down two wires to Node B and Node C; both feed Node D, which therefore runs twice.Node A emits one message down two wires to Node B and Node C; both feed Node D, which therefore runs twice.
A second wire copies the message; it does not split it.
  • A node runs when a message arrives on its input.
  • Multiple outbound wires fan out — each downstream branch receives the message. They run independently; one failing does not stop the others.
  • Multiple inbound wires mean the node runs once per arriving message, not once for all of them. Use Join when you need to wait for several branches.
  • A node returning nothing (None/null) ends that branch — a clean way to filter.

Execution context

Each execution carries an identity used for logging, correlation and lifetime:

FieldMeaning
Execution IDOne traversal of the graph
Pipeline ID / projectWhere it ran
TriggerWhat started it
Start / end time, statusOutcome

Per-node results are recorded against the execution, which is what makes a failed run reconstructable after the fact.

Timeouts

ScopeSetting
Python nodetimeout in node configuration, default 60 s
REST nodetimeout, default 30 s
Database transactiontimeoutMs, server-side auto-rollback
REST ingress routeRoute timeout, default 30 s

A node that exceeds its timeout fails, which is a failure like any other: the branch stops unless the node's error output is wired, and a Catch reports it either way. Set timeouts shorter than the caller's patience — an HTTP client that gave up long ago does not benefit from a node still running.

Errors

By default a failing node stops its branch: the error is recorded against the execution, the other branches carry on, and the pipeline stays running for the next trigger.

That default is the right one for a fault nobody planned for. For the ones you did plan for, there are two features, and they answer different questions.

Catch nodeError output port
AnswersTell somebody this brokeCarry on without it
ShapeA separate node, no input, dispatched after a failureA second socket on the node that failed
ScopeEvery node in the pipeline, or a chosen fewThat one node
The failed branchCannot be resumed — the handler is a new branchContinues, down the error wire
EnabledBy adding the nodePer node, on the Resilience (Retry) tab

They compose. A pipeline can report every failure through one Catch and recover from a specific one in flow.

The error output port New in 1.0.4

Open a node's property panel, go to Resilience (Retry), and switch Error Output on. A second socket appears on the node, drawn in red. It fires when that node has finally failed — after its retries are exhausted, not on each attempt.

┌─ out ──────────────────────────┐
Inject → fetch-price ──┤ ├→ compute → write-db
└─ error → Python (cached value) ┘

Wiring the error branch back into compute runs compute once, with whichever edge delivered — the same convergence rule that governs any other node with several inbound wires. That is the whole point of the port: recovery without duplicating the rest of the pipeline underneath a handler.

The message on the error wire is the same envelope a Catch emits — error, source and the input that caused the failure.

Four behaviours worth knowing:

  • The node reports WARNING, not ERROR. It draws amber on the canvas and stays in the run log, because a handled failure is still a failure — but it is no longer an outage, and the branch it feeds is allowed to run.
  • A routed error does not also fire a Catch. You wired the failure somewhere, so it went there.
  • An error output switched on but left unwired fails normally. Routing an error into empty space would swallow it, which is worse than the behaviour it replaces.
  • Switch has no error output. A router publishes its own ports; the two are not combined.

What a failure looks like in the log

LevelMeans
ERRORThe node failed and nothing handled it. The branch stopped
WARN 1.0.4Something went wrong and was handled — a node whose failure left through its error output, or another recoverable fault
RETRYINGAn attempt failed and another one is coming

WARN is filterable in the system log toolbar, which is the point of it: a handled fault is neither routine chatter nor an outage, and it used to be recorded as one or the other.

Writes

Failures on the way out to a device or a database are covered separately by store-and-forward: transport failures buffer and retry, data errors quarantine.

Debugging

1. Attach a Debug node. The fastest way to see what a node actually produced, in the Debug panel.

2. Trigger a single node.

Use Trigger node on the node itself (needs Run pipelines). It runs one node with its current input, without running the whole graph — the pipeline equivalent of stepping.

3. Read the execution log.

Executions, individual executions and per-node results all require View execution logs.

4. Watch node status on the canvas. Nodes show live state as messages pass through, pushed over the telemetry bus.

5. Lint Python before running. The editor lints as you type, so syntax errors surface while typing rather than at 03:00.

Performance

SymptomCauseFix
Pipeline lags behind its triggerThe graph takes longer than the intervalLengthen the interval, or split the work across pipelines
High CPU on the Python workerPer-message Python for high-rate dataFilter with RBE first; move set-based work into SQL
Database saturatedOne write per messageBatch with Join, or aggregate before writing
Memory growthVery large arrays held in memoryUse Array Iterator to stream instead of materialising
Slow tag readsA loop of single readsUse one multi-tag read (readAll)

Two rules cover most cases: filter early (drop what you do not need at the first opportunity) and batch late (combine before the expensive downstream step).

Concurrency

  • Executions of the same pipeline can overlap if the trigger fires faster than the graph completes. Design nodes to be re-entrant, or lengthen the interval.
  • Batch Transform processes items in parallel; ordering across items is not guaranteed.
  • Variables are shared across pipelines. Use system.vars.incr for counters — it is atomic; read-modify-write is not. → Variables

Starting and stopping

Starting and stopping a pipeline — individually or all of them in the project — requires Start and stop pipelines. Executing once requires Run pipelines.

Disabling a project stops its pipelines. Disabling a pipeline keeps it out of "start all" — the right way to shelve one without deleting it.

Next

Variables