Add exception handlers to action flows by right-clicking any node → Add Exception Handler. Choose the most specific exception type (User, Database, Communication, Security) before AllExceptions. Log errors to Service Center using the LogMessage or LogError built-in actions. Always show user-friendly Feedback Messages rather than raw exception details.
Error Handling in OutSystems
Unhandled exceptions in OutSystems display an ugly system error page to users and leave no trace in logs unless you explicitly handle and record them. This tutorial covers the full error handling lifecycle: catching exceptions at the right level, logging useful diagnostic information, and presenting user-friendly error messages. The patterns apply to both Client Actions (browser-side) and Server Actions (server-side).
Prerequisites
- A Reactive Web App with at least one Server Action and one Client Action that calls it
- Service Studio open with the Logic tab visible
- Basic familiarity with action flows and the Action Flow Editor
Step-by-step guide
Understand the five exception types
Understand the five exception types
OutSystems has a hierarchy of exception types. Handlers are matched from most specific to least specific: 1. User Exception — custom business exceptions you define and raise with the Raise Exception node. Example: 'InvalidOrderAmount', 'CustomerBlocked'. 2. Database Exception — database constraint violations (FK violation, unique index, NOT NULL breach), connection errors. Triggered automatically by the platform. 3. Communication Exception — network failures when calling REST/SOAP APIs, server timeouts, unreachable servers. 4. Security Exception — unauthorized access, role check failures, invalid sessions. 5. AllExceptions — catches anything not caught by a more specific handler above it. Best practice: always handle the most specific type you expect, then add an AllExceptions handler as a safety net. Handling AllExceptions alone swallows Database and Communication exceptions without distinguishing them. To define a custom User Exception: Logic tab → Exceptions → right-click → Add Exception. Name it (e.g. 'InvalidAmount'). Set BuiltIn = No.
Expected result: You can identify which exception type will be raised by any given failure scenario in your app.
Add an exception handler to a Server Action
Add an exception handler to a Server Action
Open SaveEmployee Server Action in the Action Flow Editor (Logic tab → Server Actions → double-click). Right-click any node in the flow (e.g., the CreateEmployee entity action) → 'Add Exception Handler'. A handler connector appears from the node to an Exception Handler node at the bottom of the flow. In the Exception Handler node Properties Panel, set: - Exception: Database Exception - Abort Transaction: Yes (rolls back any uncommitted DB changes) Inside the handler: Start (handler) → LogError ('SaveEmployee failed', ExceptionMessage) → Raise User Exception 'SaveFailed' with message 'Employee could not be saved. Please try again.' → End By raising a User Exception from inside the handler, you convert a technical Database Exception into a business-level exception that the Client Action above can catch with a friendly message.
1/* Exception handler flow inside SaveEmployee */2Exception Handler (Database Exception, Abort: Yes)3 --> LogError:4 ModuleName = "HRModule"5 Source = "SaveEmployee"6 Message = "DB error saving employee: " + ExceptionMessage7 --> Raise Exception: SaveFailed8 Message = "Employee could not be saved. Please try again."9 --> EndExpected result: When a database constraint fails (e.g., duplicate email), the handler fires, logs the technical error, and raises a clean SaveFailed User Exception up the call stack.
Catch the re-raised exception in the Client Action
Catch the re-raised exception in the Client Action
In the Client Action that calls SaveEmployee (e.g., SubmitFormOnClick), add an exception handler for the SaveFailed User Exception. In the Client Action flow, right-click the SaveEmployee call node → 'Add Exception Handler'. Set Exception = SaveFailed (User Exception). Inside the handler: Exception Handler (SaveFailed) --> Message: ExceptionMessage (Warning type) --> End (flow ends here, not navigating away) The ExceptionMessage variable is automatically available inside exception handlers — it holds the message string from the Raise Exception node. For handling all unexpected errors, add a second handler: right-click anywhere in the flow → 'Add Exception Handler' → set to AllExceptions. This catches anything SaveEmployee did not already handle.
1/* Client Action: SubmitFormOnClick */2Start3 --> SaveEmployee: FullName, DepartmentId4 --> Destination: EmployeeListScreen5 --> End67/* Exception handler attached to SaveEmployee node: */8Exception Handler (User Exception: SaveFailed)9 --> Message: ExceptionMessage (MessageType = Warning)10 --> End1112/* Fallback: */13Exception Handler (AllExceptions)14 --> Message: "An unexpected error occurred. Our team has been notified." (Error)15 --> EndExpected result: A database error during save shows the user a clear warning message instead of a system crash page. The unexpected error handler catches anything else.
Log errors to Service Center with LogMessage and LogError
Log errors to Service Center with LogMessage and LogError
OutSystems provides two built-in logging actions in Logic tab → System: - LogMessage(ModuleName, Source, Message): writes to the General log in Service Center. Use for informational events and warnings. - LogError(ModuleName, Source, Message, AdditionalData): writes to the Error log with full stack trace context. Use for all caught exceptions. To use LogError: drag it from Logic tab → System → Logging into the exception handler flow before re-raising or showing the message to the user. In Service Center: Monitoring → Error Logs (shows LogError entries) or Monitoring → General Logs (shows LogMessage entries). Each entry includes timestamp, module, user, environment, and message. Best practice log format: Module='HRModule', Source='SaveEmployee', Message='DB exception: ' + ExceptionMessage + ' | UserId: ' + IntegerToText(GetUserId()) + ' | Input: ' + FullName
1/* LogError call inside exception handler */2LogError:3 ModuleName = "HRModule"4 Source = "SaveEmployee"5 Message = "DB exception saving employee"6 AdditionalData = "User: " + IntegerToText(GetUserId())7 + " | Name: " + FullName8 + " | Error: " + ExceptionMessageExpected result: After triggering an error, you can see the detailed log entry in Service Center → Monitoring → Error Logs with user context and the original exception message.
Create a global error handler screen for unhandled exceptions
Create a global error handler screen for unhandled exceptions
For errors that escape all local handlers (e.g., an unhandled exception in a screen's OnInitialize action), OutSystems redirects to a default error page. You can customize this. In the Interface tab, create a new Screen named 'GlobalErrorScreen'. Set its Role access to Anonymous so any user can see it. In Service Studio: Logic tab → Exceptions → set the Error Handler property of your module to GlobalErrorScreen. Alternatively, in the Interface tab select your UI Flow → Properties Panel → set Error Handler to GlobalErrorScreen. Any unhandled exception in screens within that UI Flow will redirect here. On GlobalErrorScreen: show a user-friendly message ('Something went wrong. Please go back or contact support.') and a button to navigate to Home. Do NOT display the raw ExceptionMessage on this screen — it exposes internal details.
Expected result: Intentionally removing an exception handler and triggering an error redirects users to your branded GlobalErrorScreen instead of the OutSystems default error page.
Complete working example
1/* ============================================================2 EXCEPTION DEFINITIONS (Logic tab → Exceptions)3 - SaveFailed : User Exception4 - ValidationError : User Exception5 ============================================================ */67/* ============================================================8 SERVER ACTION: SaveEmployee9 Exception handling: Database Exception → log → re-raise10 ============================================================ */11Start12 --> Aggregate: GetEmployeeByEmail (check for duplicates)13 --> If: not GetEmployeeByEmail.List.Empty14 [True] --> Raise Exception: ValidationError15 Message = "An employee with this email already exists."16 --> CreateEmployee: Employee.FullName=FullName, Employee.Email=Email17 --> Assign: EmployeeId = CreateEmployee.Id18 --> End1920Exception Handler (Database Exception, Abort: Yes)21 --> LogError:22 ModuleName = "HRModule"23 Source = "SaveEmployee"24 Message = ExceptionMessage25 AdditionalData = "Email: " + Email26 --> Raise Exception: SaveFailed27 Message = "Could not save employee. Please try again."28 --> End2930/* ============================================================31 CLIENT ACTION: ButtonSubmitOnClick32 ============================================================ */33Start34 --> (validation logic)35 --> If: Form.Valid36 [True] --> SaveEmployee: FullName=FullName, Email=Email37 --> Destination: EmployeeListScreen38 [False]--> End39 --> End4041Exception Handler (User Exception: ValidationError)42 --> Message: ExceptionMessage (Warning)43 --> End4445Exception Handler (User Exception: SaveFailed)46 --> Message: ExceptionMessage (Warning)47 --> End4849Exception Handler (AllExceptions)50 --> Message: "An unexpected error occurred. Our team has been notified." (Error)51 --> EndCommon mistakes
Why it's a problem: Using only AllExceptions and not distinguishing Database from Communication exceptions
How to avoid: Add specific handlers for Database Exception and Communication Exception first, with appropriate logic (rollback for DB, retry message for network). Use AllExceptions only as a final fallback.
Why it's a problem: Catching an exception in a Server Action but not logging or re-raising — silently swallowing it
How to avoid: An empty exception handler (handler → End) hides errors completely. Always either log the error, re-raise a meaningful exception, or at minimum show an output flag indicating failure to the caller.
Why it's a problem: Placing LogError inside a Client Action exception handler for server-side errors
How to avoid: LogError from a Client Action writes to the browser console, not Service Center. Log server-side errors in Server Action exception handlers before re-raising. Client Action handlers should only handle User Exceptions that bubbled up from server calls.
Why it's a problem: Not setting Abort Transaction = Yes in a Database Exception handler that catches a partial write
How to avoid: Without Abort Transaction, partial writes made before the exception may be committed. Set Abort Transaction = Yes in any handler where database integrity could be compromised.
Best practices
- Always log the ExceptionMessage in server-side exception handlers before showing a user-friendly message — raw technical details belong in logs, not in the UI.
- Handle the most specific exception type (Database, Communication) before the general AllExceptions handler in the same action.
- Set Abort Transaction = Yes in Database Exception handlers to prevent partial data commits.
- Re-raise a User Exception from a Server Action's Database Exception handler so the calling Client Action receives a business-level error it can display meaningfully.
- Never display ExceptionMessage directly to end users on error screens — it may expose internal table names, SQL fragments, or stack traces.
- Test your exception handlers by intentionally triggering the error condition in the Debugger (Processes tab → Debugger) to verify the handler fires correctly.
Still stuck?
Copy one of these prompts to get a personalized, step-by-step explanation.
Explain OutSystems exception handling for a Reactive Web App. I have a Server Action SaveEmployee that writes to the database. Show me how to: (1) add a Database Exception handler with Abort Transaction and LogError, (2) re-raise a custom SaveFailed User Exception, (3) catch SaveFailed in the calling Client Action and show a warning Message, (4) add an AllExceptions fallback. Use OutSystems action flow arrow notation.
Add error handling to my OutSystems SaveEmployee Server Action. Create a Database Exception handler with Abort Transaction = Yes that calls LogError (ModuleName='HRModule', Source='SaveEmployee', Message=ExceptionMessage) and then raises a SaveFailed User Exception with message 'Could not save employee. Please try again.' In the calling Client Action ButtonSubmitOnClick, add a SaveFailed handler showing ExceptionMessage as a Warning Message, and an AllExceptions handler showing a generic error message.
Frequently asked questions
What is the difference between LogMessage and LogError in OutSystems?
LogMessage writes to the General log in Service Center and is for informational events (workflow started, record processed). LogError writes to the Error log with a higher severity level and is specifically for caught exceptions. Both are in Logic tab → System → Logging. Use LogError for all exception handlers.
Can I catch exceptions in a Data Action?
Yes. Data Actions run server-side and support the same exception handler mechanism as Server Actions. Right-click any node in a Data Action flow → Add Exception Handler. Unhandled Data Action exceptions cause the screen's data-fetch to fail silently — always add at least an AllExceptions handler in Data Actions used in production.
How do I handle exceptions differently in development vs production without changing code?
Use a Site Property 'IsDebugMode' (Boolean, set True in Dev, False in Prod). In AllExceptions handlers, check If IsDebugMode then show ExceptionMessage in the feedback message, else show the generic message. Change the Site Property value per environment in Service Center without redeploying.
Does ODC have a different logging mechanism than O11?
ODC uses the same LogMessage and LogError actions in your action flows, but logs are viewed in ODC Portal → Monitoring → Traces instead of Service Center. The action signatures and behavior are identical. ODC also provides structured logging with automatic correlation IDs for distributed tracing.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation