Node Event Subscriptions Overview
Add cross-cutting behaviour to every node execution — audit logging, metrics collection, notifications — without touching executor code. INodeEventSubscriber is the observer pattern for the execution pipeline.
Why Subscribers Exist
When you need the same behaviour applied to every node execution — write an audit record, increment a counter, send a Slack alert on failure — the wrong place to implement it is inside each executor. That spreads identical logic across dozens of classes and makes it impossible to change consistently.
INodeEventSubscriber solves this with the observer pattern. Subscribers are registered once in DI and the engine calls them automatically at four points in the lifecycle of every node execution, regardless of which executor is running.
Before Execution
Called before the executor runs. Use for audit start records, pre-execution validation checks, or request enrichment.
After Execution
Called after a successful result. Full output is available. Use for metrics recording, downstream notifications, SLA tracking.
On Error
Called when the executor throws. The exception and retry context are available. Use for alerting, error catalogue logging.
On Skipped
Called when a node is skipped (conditional routing). Use for completeness auditing — every node's outcome is recorded.
Subscriber vs. GuardRail Engine
A common question: when do I use a subscriber vs. a GuardRail engine? The distinction is clear:
| Concern | Use Subscriber | Use GuardRail Engine |
|---|---|---|
| Audit logging | Yes | No |
| Metrics / telemetry | Yes | No |
| Block execution based on policy | No | Yes |
| Rate limiting | No | Yes |
| Post-execution notifications | Yes | No |
| PII content filtering | No | Yes |
OnBeforeExecuteAsync will abort the node, that pattern is reserved for GuardRail engines. Use subscribers for observation, not enforcement.
The Four Lifecycle Events
OnBeforeExecuteAsync
Engine is about to call the executor. Context snapshot and input data are available.
OnAfterExecuteAsync
Executor returned successfully. Full NodeExecutionResult including output is available.
OnErrorAsync
Executor threw an exception. The exception, retry count, and context are available.
OnSkippedAsync
Node was skipped by the router (conditional branching). Skip reason is provided.
Execution Order
When multiple subscribers are registered, all are called for each event in the order they were registered in DI. There is no short-circuit — if subscriber A throws during OnAfterExecuteAsync, subscriber B is still called. Subscriber exceptions are caught and logged but do not affect workflow execution.
Common Use Cases
| Use Case | Events Used | Typical Implementation |
|---|---|---|
| Centralised audit log | Before + After + Error + Skipped | Write one row per lifecycle event to audit_node_executions |
| Duration metrics | Before (record start) + After (compute delta) | Store start time in a dictionary keyed by executionId+nodeId |
| Failure alerting | OnError | Enqueue a message to the alerting queue |
| Execution tracing | All four | Attach structured log entries to the OpenTelemetry span |
| SLA tracking | Before + After | Compare elapsed time against node SLA configuration |