Error Handling
Workflow Error Handling
Error handling in Rhythm is designed to feel like standard asynchronous programming. Because Rhythm is a durable execution framework, when an error is caught in a .flow script, the error state and the subsequent recovery logic are persisted to the database, ensuring your workflow remains reliable even after a crash or restart.
Using Try/Catch Blocks
The primary way to handle errors within a workflow is using try/catch blocks. You can wrap any statement—most commonly await calls to Tasks or Signals—to intercept failures and execute fallback logic.
try {
// Attempt a task that might fail
await Task.run("process-payment", { amount: 100 })
} catch (err) {
// Handle the failure
await Task.run("notify-failure", {
reason: err.message,
code: err.type
})
}
Task Failures
When a Task fails in your application code (e.g., in Python or Rust), the Rhythm worker reports the failure back to the engine. The engine then propagates this failure into the workflow script as a catchable error.
The err object in the catch block typically contains:
type: A string identifier for the error (e.g., a specific error code or exception name).message: A human-readable description of what went wrong.
Signal Timeouts
Signals are often used to wait for external events. If a signal includes a timeout and that period elapses without the signal being received, the engine throws a timeout error.
try {
// Wait for 24 hours for a manager to approve
const approval = await Signal.when("manager-approval", { timeout: "24h" })
} catch (err) {
if (err.type === "Timeout") {
await Task.run("escalate-to-admin", { originalWorkflow: Workflow.id })
}
}
Execution Status and Retries
When an error occurs and is not caught by a try/catch block, the workflow execution transitions to a Failed status.
- Persistence: The error details (message and type) are stored in the
outputfield of the execution record in Postgres. - Retries: While Rhythm tracks the number of
attemptson an execution, the engine currently treats unhandled errors as terminal for that specific execution instance. To retry a failed workflow, you must trigger a new execution or implement a retry loop within the.flowlogic itself:
let success = false
let retries = 0
while (!success && retries < 3) {
try {
await Task.run("unstable-integration")
success = true
} catch (err) {
retries = retries + 1
// Optional: wait before retrying (if Sleep/Timer is implemented)
}
}
if (!success) {
return { status: "failed_after_retries" }
}
Validation Errors
Rhythm performs static analysis and validation. Certain errors are thrown by the engine before or during execution if the script violates engine constraints:
| Error Code | Description |
| :--- | :--- |
| NOT_A_FUNCTION | Attempting to call a value that is not a callable task or built-in. |
| WRONG_ARG_COUNT | Calling a standard library function with the incorrect number of arguments. |
| WRONG_ARG_TYPE | Passing an invalid type (e.g., passing a string to Math.floor). |
These logic errors should typically be resolved during development/testing as they represent bugs in the workflow definition rather than transient operational failures.