Portal Community
All examples below show a complete Try/Catch/Finally trio. The Finally Block node itself has no config — the value comes from the nodes connected to its success port.

01 Unconditional Audit Log with Outcome Detection

A compliance-regulated data export workflow must record every export attempt in an audit log — success or failure. The finally scope uses an If Condition to detect the outcome and writes the appropriate audit record. This single finally block replaces duplicated logging logic in both the success and error paths.

// Finally Block
[Finally Block: "finally_export_audit"]
  → [If Condition: "$mem.__exception_type__ != null"]
      // true (error occurred):
      → [DB Insert: audit_log {
            "workflow": "data-export",
            "run_id": "{{ $ctx.workflow_instance_id }}",
            "outcome": "FAILED",
            "error": "{{ $mem.__exception_message__ }}",
            "records_processed": "{{ $var.records_processed }}",
            "timestamp": "{{ $now }}"
         }]
      // false (success):
      → [DB Insert: audit_log {
            "workflow": "data-export",
            "run_id": "{{ $ctx.workflow_instance_id }}",
            "outcome": "SUCCESS",
            "error": null,
            "records_processed": "{{ $var.records_processed }}",
            "timestamp": "{{ $now }}"
         }]
Outcome: The audit_log table always has a record for every export run. Compliance auditors can query by workflow and date range to verify all export activity, including failed attempts. Both records carry the run ID for correlation with the full workflow execution log.

02 Release Database Transaction Lock

A bulk update workflow acquires an exclusive lock on a database table at the start. The finally block unconditionally releases the lock, ensuring other workflows are never left waiting due to an exception mid-processing.

// Full trio structure:
[Try Block: "try_bulk_update"]
  → [DB: Acquire Table Lock on orders_pending]
  → [Loop: Process each order record]
      → [DB: Update order status]
  → [DB: Commit Transaction]
[Catch Block: "catch_bulk_update"]
  → [DB: Rollback Transaction]
  → [Slack: "Bulk update failed: {{ $mem.__exception_message__ }}"]
[Finally Block: "finally_bulk_update"]
  → [DB: Release Table Lock on orders_pending]
  → [Log: "Table lock released for run {{ $ctx.workflow_instance_id }}"]
Outcome: Whether the bulk update succeeds and commits, or fails and rolls back, the table lock is always released. No downstream workflow or manual query is ever blocked by a forgotten lock from a failed workflow run. The release is logged for the database administrator's monitoring.

03 Update Processing Status Record

A long-running document processing workflow maintains a status record in a jobs table (status: "processing"). The finally block always updates this record to either "completed" or "failed", preventing status records from permanently showing "processing" after a failure.

// Before try block:
[DB Update: jobs { status: "processing", started_at: "{{ $now }}" } WHERE id = {{ $var.job_id }}]

[Try Block: "try_document_processing"]
  → [Download Document]
  → [Extract Text]
  → [AI Classify Document]
  → [Store Results]
[Catch Block: "catch_document_processing"]
  → [Log Error: "{{ $mem.__exception_type__ }}: {{ $mem.__exception_message__ }}"]
[Finally Block: "finally_document_processing"]
  → [If Condition: "$mem.__exception_type__ != null"]
      // error path:
      → [DB Update: jobs {
            "status": "failed",
            "completed_at": "{{ $now }}",
            "error_message": "{{ $mem.__exception_message__ }}"
         } WHERE id = {{ $var.job_id }}]
      // success path:
      → [DB Update: jobs {
            "status": "completed",
            "completed_at": "{{ $now }}",
            "error_message": null
         } WHERE id = {{ $var.job_id }}]
Outcome: The jobs table accurately reflects the state of every job. No job is permanently stuck in "processing" after a failure. Monitoring dashboards and retry mechanisms that query the jobs table operate on accurate data. Failed jobs are immediately visible and queryable for retry or escalation.

04 Emit SIEM Audit Event

A security-sensitive workflow (privileged access request processing) emits a structured audit event to the organisation's SIEM platform after every execution, regardless of outcome. This is a compliance requirement — even failed attempts must be recorded.

[Finally Block: "finally_access_request_audit"]
  → [HTTP Request: POST to SIEM Event Ingestion API {
        "event_type": "PRIVILEGED_ACCESS_WORKFLOW",
        "workflow_id": "{{ $ctx.workflow_definition_id }}",
        "run_id": "{{ $ctx.workflow_instance_id }}",
        "user": "{{ $var.requestor_id }}",
        "resource": "{{ $var.requested_resource }}",
        "outcome": "{{ $mem.__exception_type__ != null ? 'FAILED' : 'COMPLETED' }}",
        "error": "{{ $mem.__exception_message__ }}",
        "timestamp": "{{ $now }}"
     }]
Outcome: The SIEM has a complete, tamper-evident record of every privileged access workflow execution. Security analysts can alert on unusual patterns (high failure rates, off-hours access attempts, specific user behaviour) in real time. The ternary expression in the outcome field produces the correct status string without requiring two separate HTTP request nodes.

05 Clean Up Temporary Storage Files

A report generation workflow creates several temporary files during processing (intermediate data exports, chart images, staging files). The finally block deletes all temporary files unconditionally, preventing orphaned files from accumulating in storage regardless of whether report generation succeeded or failed.

[Try Block: "try_report_generation"]
  → [Export Raw Data to temp CSV: $var.temp_csv_path]
  → [Generate Charts: store in $var.temp_chart_paths]
  → [Merge into PDF: $var.final_report_path]
  → [Upload to Report Server]
[Catch Block: "catch_report_generation"]
  → [Slack: "Report generation failed: {{ $mem.__exception_message__ }}"]
[Finally Block: "finally_report_cleanup"]
  → [File Delete: "{{ $var.temp_csv_path }}"]
  → [Loop: $var.temp_chart_paths]
      → [File Delete: "{{ $loop.current }}"]
  → [Log: "Temporary files cleaned up for run {{ $ctx.workflow_instance_id }}"]
Outcome: All temporary files are deleted after every run — whether the report was generated successfully or failed partway through. Storage costs remain predictable. A storage audit at any point shows no orphaned temporary files from failed workflow runs.