$exec — Execution Directive
Reads runtime metadata about the current workflow execution instance — IDs, timing, trigger info, and retry state.
| Property | Value |
|---|---|
| Directive name | exec (or execFacts in current implementation) |
| Required isolation level | Safe |
| Project | FlowExecution.Services |
| Data source | INodeExecutionFacts |
Complete Path Reference
| Path | Type | Example | Description |
|---|---|---|---|
$exec.executionId | String (GUID) | e8f23a1b-... | Unique ID for this execution instance |
$exec.threadId | String | t_main | Current execution thread identifier |
$exec.nodeKey | String | SendInvoiceEmail | Key of the currently executing node |
$exec.startedAt | String (ISO 8601) | 2024-03-15T10:22:01Z | When this execution started |
$exec.triggeredBy | String | Webhook | Trigger type: Manual / Webhook / Scheduled / Form |
$exec.triggeredByUserId | Number (nullable) | 42 | ID of user who triggered (null for scheduled/webhook) |
$exec.retryCount | Number | 0 | How many times this node has been retried |
$exec.isRetry | Boolean | false | true if this is a retry attempt |
$exec.parentExecutionId | String (nullable) | d3a8... | ID of the parent execution (set for sub-workflows) |
$exec.correlationId | String (nullable) | ORDER-2024-001 | Business correlation ID passed at trigger time |
Complex Scenarios
Scenario 1 — Idempotency Key for External API Calls
Use the execution ID as an idempotency key to prevent duplicate submissions on retry:
HTTP header: Idempotency-Key
{@ $exec.executionId }-{@ $exec.nodeKey }
e8f23a1b-4c2d-...-SendPayment — unique per execution AND node
Scenario 2 — Skipping Expensive Operations on Retry
If a node is being retried, skip a slow data-fetch step that already succeeded:
// IfCondition: "Skip if retry"
{@ $exec.isRetry }
// true → skip to next node (data already fetched in first attempt)
// false → run the full data fetch
// More nuanced: only skip if retried more than once
{@ $js`return context.exec.retryCount > 1` }
Scenario 3 — Structured Log Entry with Correlation
// Structured audit log node field:
{
"timestamp": "{@ $ctx.now }",
"executionId": "{@ $exec.executionId }",
"correlationId": "{@ $exec.correlationId @default:none }",
"workflow": "{@ $flow.current.name }",
"node": "{@ $exec.nodeKey }",
"triggeredBy": "{@ $exec.triggeredBy }",
"userId": {@ $exec.triggeredByUserId @default:null }
}
Scenario 4 — Sub-workflow Traceability
When a sub-workflow runs, it can reference its parent execution for full traceability:
// Sub-workflow creates a child execution record linking to parent:
POST /api/audit/execution
{
"executionId": "{@ $exec.executionId }",
"parentExecutionId": "{@ $exec.parentExecutionId @default:root }",
"workflowId": "{@ $flow.current.id }",
"tenantId": {@ $ctx.tenant.id }
}
Scenario 5 — Time-Based Execution Reporting
Build a duration report by comparing execution start to current time:
{@ $js`
const started = new Date(context.exec.startedAt);
const now = new Date(context.ctx.now);
const seconds = Math.round((now - started) / 1000);
return seconds < 60 ? seconds + 's' : Math.round(seconds/60) + 'm';
` }
// "47s" or "3m" — human-readable execution duration so far
Common Errors
| Error | Cause | Fix |
|---|---|---|
PathNotFound: correlationId | No correlation ID was passed at trigger time | Use @default:none; make correlation ID optional in your design |
PathNotFound: parentExecutionId | This is a top-level execution, not a sub-workflow | Use @default to handle both top-level and sub-workflow contexts |