Portal Community
  The FinallyBlock ALWAYS executes — on both the success path and the exception path.

When to Use

Configuration

No configuration required. FinallyBlock has no settings. Place it after the CatchBlock in the workflow. Its success port fires automatically at the end of both the success path and the catch path.

Execution Flow

Success Path (no exception)

  1. TryBlock executes
  2. All try-body nodes run normally
  3. CatchBlock is skipped
  4. FinallyBlock fires
  5. Workflow continues after finally

Exception Path

  1. TryBlock executes
  2. A node throws an exception
  3. CatchBlock fires, handles error
  4. FinallyBlock fires
  5. Workflow continues (or stops if Behavior=stop)

What FinallyBlock Does Internally

When FinallyBlock executes, the engine performs the following cleanup operations before firing the success port:

  1. Calls Memory.ExitTryBlock() — pops the try/catch frame from the exception handler stack, re-enabling normal exception propagation for any exceptions thrown after the finally scope.
  2. Calls Memory.ExitVariableScope() — closes the variable scope opened by TryBlock. Variables that were scoped to the try/catch/finally block are removed from ExecutionMemory.
  3. Clears CurrentException — removes the current exception object from ExecutionMemory so downstream nodes (after finally) do not accidentally see a stale exception reference.

Output Ports

PortDescription
successFires after FinallyBlock completes its internal cleanup and all finally-scope nodes have executed. Downstream nodes connected here run as normal sequential workflow nodes — they are outside the try/catch/finally context.

OutputData Fields

FinallyBlock itself writes no data fields. All variables set during the try and catch scopes that are still in-scope at finally time are accessible via $var. Exception variables (__exception_type__, __exception_message__, __exception_stacktrace__) are readable inside the finally scope if an exception was caught — they are cleared from memory after FinallyBlock completes.

Reading Exception State Inside FinallyBlock

Use exception variables inside the finally scope to write outcome-sensitive audit entries without duplicating the logging node:

ExpressionUse in FinallyBlock
{{ $var.__exception_type__ !== undefined }}Condition: was an exception caught? Use in an IfCondition to choose the outcome label for the audit log.
{{ $var.__exception_message__ }}Include the error message in the audit record when an exception occurred.

Node Policies and GuardRails

Pattern Examples

Pattern 1 — Guaranteed Resource Release

A workflow acquires an exclusive processing slot before entering the TryBlock. The FinallyBlock always releases it, preventing slot starvation for other workflow instances.

VariableAssignment  (acquiredSlot = true)
HttpRequest  (POST /slots/acquire)
  └─► TryBlock  [key: "processTry"]
        └─► success ──► [... processing nodes ...]
CatchBlock  [key: "processCatch"]
  Behavior: continue
  └─► success ──► MongoDB  (log error: {{ $var.__exception_message__ }})
FinallyBlock  [key: "processFinally"]
  └─► success ──► HttpRequest  (POST /slots/release)   (ALWAYS runs)
                    └─► [continue workflow]

Pattern 2 — Outcome-Sensitive Audit Log

Write a single audit log entry whose outcome field reflects success or failure without duplicating the logging node.

TryBlock  [key: "auditTry"]
  └─► success ──► [core business logic]
CatchBlock  [key: "auditCatch"]
  Behavior: continue
  └─► success ──► VariableAssignment (outcomeStatus = 'failed')
FinallyBlock  [key: "auditFinally"]
  └─► success ──► IfCondition  [key: "setOutcome"]
                    Condition: {{ $var.outcomeStatus !== 'failed' }}
                    ├─► true  ──► VariableAssignment (outcomeStatus = 'succeeded')
                    └─► false ──► (already set to 'failed' by catch)
                    [both paths merge to:]
                    └─► MongoDB  (insert audit_log: {
                                   workflowId: {{ $ctx.workflowId }},
                                   outcome:    {{ $var.outcomeStatus }},
                                   ts:         {{ $now }}
                                 })

Pattern 3 — Complete Try/Catch/Finally for External Payment API

Full pattern showing all three nodes working together: TryBlock protects the API call, CatchBlock alerts the team on failure, FinallyBlock updates the payment status record unconditionally.

TryBlock  [key: "payTry"]
  └─► success ──► HttpRequest  [key: "chargeApi"]
                    POST /payments/charge
                    { amount: {{ $json.amount }}, token: {{ $json.paymentToken }} }
                    └─► VariableAssignment
                          paymentStatus = 'succeeded'
                          chargeId = {{ $output.chargeApi.chargeId }}

CatchBlock  [key: "payCatch"]
  Behavior: continue
  └─► success ──► VariableAssignment (paymentStatus = 'failed')
                    └─► SlackNode  (#payments-alerts)
                          "Payment failed for order {{ $json.orderId }}:
                           {{ $var.__exception_type__ }}
                           {{ $var.__exception_message__ }}"

FinallyBlock  [key: "payFinally"]
  └─► success ──► MongoDB  (update orders SET
                             payment_status = {{ $var.paymentStatus }},
                             charge_id = {{ $var.chargeId }},
                             updated_at = {{ $now }}
                             WHERE id = {{ $json.orderId }})
                    └─► [workflow continues: route on $var.paymentStatus]

Sample Output

Success Port — Always Runs

{
  "_finallyContext": {
    "tryCaughtError": false,
    "lastNodeOutput": {
      "invoiceId": "INV-2025-00123",
      "status": "processed",
      "amount": 4500.00
    },
    "executionPath": "try_success",
    "workflowId": "wf_invoice_processing",
    "executionId": "exec_20250315_001"
  }
}

Error Scenario — After CatchBlock

{
  "_finallyContext": {
    "tryCaughtError": true,
    "caughtError": {
      "errorCode": "VALIDATION_FAILED",
      "message": "Invoice total mismatch",
      "nodeName": "validateInvoice"
    },
    "executionPath": "catch_handled",
    "workflowId": "wf_invoice_processing"
  }
}

Expression Reference

ExpressionDescription
{{ $output.finallyNode._finallyContext.tryCaughtError }}true if an error was caught in the CatchBlock; false if Try succeeded.
{{ $output.finallyNode._finallyContext.executionPath }}"try_success" or "catch_handled" — indicates which path was taken.
{{ $output.finallyNode._finallyContext.caughtError.errorCode }}Error code from the caught exception, if any.