try

Catch pipeline errors

try

Normally when an action errors the pipeline execution is stopped immediately and a protocol specific error response is sent to the client. The try action can be used to convert the errors to ignore results.

Syntax

try <action>;
try <action> { }

Example

try backend "STORE_ACCOUNTING";
try backend {
  name "SQL_BACKEND";
  query "FIND_USER";
}

When combined with the first action, the try action can be used to fallback to a secondary action when the primary action fails:

first {
  try backend "PRIMARY_AUTH";
  try backend "SECONDARY_AUTH";
  error "All authentication backends failed";
}

Long form syntax

The try action also supports a long form syntax which includes a catch block which can be used for custom error handling

try [pipeline directive] {
    # actions
} catch {
    # Execute this pipeline if an error occurs in the main pipeline
}

When an error occurs in the main try pipeline, the actions in the catch block are executed. If no error occurs, the catch block is skipped. On error the result of the try action is the result of the last action in the catch block.

NOTE: If the try-block contains multiple actions, the execution is immediately moved to the catch-block upon the first error encountered. Actions after the error in the try-block are not executed.

try {
    backend "PRIMARY_AUTH"; # This action errors
    backend "SECONDARY_AUTH"; # This action is not executed!
} catch {
    reject "operation failed";
}

Generally it is recommended to keep only one action in the try-block to avoid confusion.

Error Information

When an error is caught, the error message is made available in the aaa.caught_error variable. This variable contains only the most recent error message.

try {
    backend "FIND_USER";
} catch {
    reject "operation failed: %{aaa.caught_error}";
}
Error History

The aaa.caught_errors variable provides access to all caught errors as a collection. Unlike aaa.caught_error, which only contains the most recent error, aaa.caught_errors accumulates all errors caught during the request processing. This is useful when multiple try actions are used and you need to track all errors that occurred.

try backend "PRIMARY_AUTH";
try backend "SECONDARY_AUTH";

# Check if any errors occurred
if any {
  aaa.caught_errors != none
} then {
    debug "Errors occurred: %{aaa.caught_errors}";
}

# Get the count of caught errors
if all {
  aaa.caught_errors|count == 2
} then {
    debug "Both backends failed";
}
Error Codes

Some errors carry a machine-readable error code accessible via aaa.caught_error.code. This allows pipelines to branch on specific error conditions rather than parsing the human-readable error message string.

Available error codes are provided as compile-time-validated constants in the errors namespace (see Execution Context for the full list).

try backend "REVERSE_RADSEC";

if all {
    aaa.caught_error.code == errors.RADIUS_IDENTIFIERS_EXHAUSTED;
} then {
    # All 256 RADIUS identifiers are in use on the connection. Respond with 429
    # Too Many Requests to signal backpressure for the sending client.
    modify {
        http.status = http.TOO_MANY_REQUESTS;
        http.response_header.Reply-After = 10;
    }
    reject;
} else if all {
    aaa.caught_error != none;
} then {
    # Handle other errors
    reject "operation failed: %{aaa.caught_error}";
}
Clearing Errors

Both aaa.caught_error and aaa.caught_errors can be cleared manually using the modify action:

modify {
    aaa.caught_error = none;
    aaa.caught_errors = none;
}

When aaa.caught_error is cleared, aaa.caught_error.code is also cleared automatically.

Pipeline Directive

You may optionally specify a pipeline directive for the try block to control how non-error results are handled. If no pipeline directive is specified, the default while directive is used. See the documentation for pipeline directives

try first {
    pap;
    chap;
} catch {
    # Error handling...
}