When your code fails, Restate automatically retries the invocation. Understanding when to stop retrying is critical to building reliable applications.Documentation Index
Fetch the complete documentation index at: https://docs.restate.dev/llms.txt
Use this file to discover all available pages before exploring further.
Check out the Error Handling guide to learn more about how Restate handles transient errors, terminal errors, retries, and timeouts.
Understanding Retryable vs Terminal Errors
Retryable errors are temporary problems that might succeed if retried:- Database connection timeout
- Network issues
- Temporary service unavailability
- Invalid user input (wrong format, missing required fields)
- Authorization failures (user doesn’t have permission)
- Business logic violations (insufficient balance, duplicate order)
TerminalErrors.
Raising TerminalError
Use TerminalError to signal permanent failures.
When you raise TerminalError in your handler code, it stops the invocation and marks it as permanently failed:
TerminalError inside a ctx.run block, it fails that specific step, allowing your handler to continue and handle the error:
ctx.run, all errors are retried unless you raise TerminalError explicitly or set up a run retry policy.
Common use cases for terminal error are:
- Input validation failures:
raise TerminalError("Invalid email format") - Business rule violations:
raise TerminalError("Insufficient balance") - Resource not found:
raise TerminalError("User ID not found")
When you throw a terminal error, you may need to undo previous actions to keep your system consistent. Check out our sagas guide to learn about compensations.
Handling Errors
Most of the time, you only need to catchTerminalError to handle permanent failures.
❌ Wrong - This leads to unexpected behavior:
Advanced: using finally for resource cleanup
Advanced: using finally for resource cleanup
Only use
finally blocks if you’re managing resources (files, connections, locks) that must be released even when retries happen.finally, understand what goes where:restate.is_internal_exception(e) to identify whether an exception is a Restate SDK internal exception that should be ignored.Key principle:except TerminalError: For compensations and business logic when the invocation permanently failsfinally: For releasing resources (files, connections) acquired during the current attempt
Retryable errors with custom delay
UseRetryableError to signal that Restate should retry the invocation with a specific delay.
This is useful when you receive a Retry-After header from an external API or want to control the retry timing explicitly.
RetryableError accepts the following parameters:
message: The error messagestatus_code: HTTP status code (default:500)retry_after: Atimedeltaspecifying when Restate should retry (optional)
ctx.run block, you can combine RetryableError with RunOptions to control both the retry delay and the maximum number of retries:
Unlike
TerminalError which stops retries permanently, RetryableError tells Restate to retry after the specified delay. If no retry_after is provided, Restate uses its default retry policy.