docs: improve documetation around error handling in workflows (#12288)

* docs: improve documetation around error handling in workflows

* regenerate
This commit is contained in:
Shahed Nasser
2025-04-24 18:41:28 +03:00
committed by GitHub
parent 8aed57d453
commit 35d7c143ea
11 changed files with 15056 additions and 14582 deletions

View File

@@ -1,52 +0,0 @@
export const metadata = {
title: `${pageNumber} Access Workflow Errors`,
}
# {metadata.title}
In this chapter, youll learn how to access errors that occur during a workflows execution.
## How to Access Workflow Errors?
By default, when an error occurs in a workflow, it throws that error, and the execution stops.
You can configure the workflow to return the errors instead so that you can access and handle them differently.
For example:
export const highlights = [
["11", "errors", "`errors` is an array of errors that occur during the workflow's execution."],
["14", "throwOnError", "Specify that errors occuring during the workflow's execution should be returned, not thrown."],
]
```ts title="src/api/workflows/route.ts" highlights={highlights} collapsibleLines="1-6" expandButtonLabel="Show Imports"
import type {
MedusaRequest,
MedusaResponse,
} from "@medusajs/framework/http"
import myWorkflow from "../../../workflows/hello-world"
export async function GET(
req: MedusaRequest,
res: MedusaResponse
) {
const { result, errors } = await myWorkflow(req.scope)
.run({
// ...
throwOnError: false,
})
if (errors.length) {
return res.send({
errors: errors.map((error) => error.error),
})
}
res.send(result)
}
```
The object passed to the `run` method accepts a `throwOnError` property. When disabled, the errors are returned in the `errors` property of `run`'s output.
The value of `errors` is an array of error objects. Each object has an `error` property, whose value is the name or text of the thrown error.

View File

@@ -199,7 +199,7 @@ You then use the logger to log a message.
---
## Handle Errors in Loops
## Handle Step Errors in Loops
<Note>
@@ -274,3 +274,9 @@ try {
The `StepResponse.permanentFailure` fails the step and its workflow, triggering current and previous steps' compensation functions. The `permanentFailure` function accepts as a first parameter the error message, which is saved in the workflow's error details, and as a second parameter the data to pass to the compensation function.
So, if an error occurs during the loop, the compensation function will still receive the `prevData` variable to undo the changes made before the step failed.
<Note>
For more details on error handling in workflows and steps, check the [Handling Errors chapter](../errors/page.mdx).
</Note>

View File

@@ -83,7 +83,7 @@ const myWorkflow = createWorkflow(
})
```
### Create Dates in transform
#### Create Dates in transform
When you use `new Date()` in a workflow's constructor function, the date is evaluated when Medusa creates the internal representation of the workflow, not during execution.
@@ -175,7 +175,7 @@ Learn more about why you can't use conditional operators [in this chapter](../co
Instead, use `transform` to store the desired value in a variable.
### Logical Or (||) Alternative
#### Logical Or (||) Alternative
```ts
// Don't
@@ -201,7 +201,7 @@ const myWorkflow = createWorkflow(
})
```
### Nullish Coalescing (??) Alternative
#### Nullish Coalescing (??) Alternative
```ts
// Don't
@@ -227,7 +227,7 @@ const myWorkflow = createWorkflow(
})
```
### Double Not (!!) Alternative
#### Double Not (!!) Alternative
```ts
// Don't
@@ -259,7 +259,7 @@ const myWorkflow = createWorkflow(
})
```
### Ternary Alternative
#### Ternary Alternative
```ts
// Don't
@@ -293,7 +293,7 @@ const myWorkflow = createWorkflow(
})
```
### Optional Chaining (?.) Alternative
#### Optional Chaining (?.) Alternative
```ts
// Don't
@@ -325,7 +325,11 @@ const myWorkflow = createWorkflow(
})
```
### No Try-Catch
In a workflow, don't use try-catch blocks to handle errors.
Instead, refer to the [Error Handling](../errors/page.mdx) chapter for alternative ways to handle errors.
---

View File

@@ -0,0 +1,303 @@
import { TypeList, Table } from "docs-ui"
export const metadata = {
title: `${pageNumber} Error Handling in Workflows`,
}
# {metadata.title}
In this chapter, youll learn about what happens when an error occurs in a workflow, how to disable error throwing in a workflow, and try-catch alternatives in workflow definitions.
## Default Behavior of Errors in Workflows
When an error occurs in a workflow, such as when a step throws an error, the workflow execution stops. Then, [the compensation function](../compensation-function/page.mdx) of every step in the workflow is called to undo the actions performed by their respective steps.
The workflow's caller, such as an API route, subscriber, or scheduled job, will also fail and stop execution. Medusa then logs the error in the console. For API routes, an appropriate error is returned to the client based on the thrown error.
<Note>
Learn more about error handling in API routes in the [Errors chapter](../../api-routes/errors/page.mdx).
</Note>
This is the default behavior of errors in workflows. However, you can configure workflows to not throw errors, or you can configure a step's internal error handling mechanism to change the default behavior.
---
## Disable Error Throwing in Workflow
When an error is thrown in the workflow, that means the caller of the workflow, such as an API route, will fail and stop execution as well.
While this is the common behavior, there are certain cases where you want to handle the error differently. For example, you may want to check the errors thrown by the workflow and return a custom error response to the client.
The object parameter of a workflow's `run` method accepts a `throwOnError` property. When this property is set to `false`, the workflow will stop execution if an error occurs, but the Medusa's workflow engine will catch that error and return it to the caller instead of throwing it.
For example:
export const highlights = [
["11", "errors", "`errors` is an array of errors that occur during the workflow's execution."],
["14", "throwOnError", "Specify that errors occuring during the workflow's execution should be returned, not thrown."],
]
```ts title="src/api/workflows/route.ts" highlights={highlights} collapsibleLines="1-6" expandButtonLabel="Show Imports"
import type {
MedusaRequest,
MedusaResponse,
} from "@medusajs/framework/http"
import myWorkflow from "../../../workflows/hello-world"
export async function GET(
req: MedusaRequest,
res: MedusaResponse
) {
const { result, errors } = await myWorkflow(req.scope)
.run({
// ...
throwOnError: false,
})
if (errors.length) {
return res.send({
message: "Something unexpected happened. Please try again.",
})
}
res.send(result)
}
```
You disable throwing errors in the workflow by setting the `throwOnError` property to `false` in the `run` method of the workflow.
The object returned by the `run` method contains an `errors` property. This property is an array of errors that occured during the workflow's execution. You can check this array to see if any errors occurred and handle them accordingly.
An error object has the following properties:
<TypeList
types={[
{
type: "`string`",
name: "action",
description: "The ID of the step that threw the error.",
},
{
type: "`invoke` \\| `compensate`",
name: "handlerType",
description: "Where the error occurred. If the value is `invoke`, it means the error occurred in a step. Otherwise, the error occurred in the compensation function of a step.",
},
{
type: "[Error](https://nodejs.org/docs/latest-v20.x/api/errors.html#class-error)",
name: "error",
description: "The error object that was thrown.",
}
]}
sectionTitle="Disable Error Throwing in Workflow"
/>
---
## Try-Catch Alternatives in Workflow Definition
<Note title="Tip">
If you want to use try-catch mechanism in a workflow to undo step actions, use a [compensation function](../compensation-function/page.mdx) instead.
</Note>
### Why You Can't Use Try-Catch in Workflow Definitions
Medusa creates an internal representation of the workflow definition you pass to `createWorkflow` to track and store its steps.
At that point, variables in the workflow don't have any values. They only do when you execute the workflow.
So, try-catch blocks in the workflow definition function won't have an effect, as at that time the workflow is not executed and errors are not thrown.
You can still use try-catch blocks in a workflow's step functions. For cases that require granular control over error handling in a workflow's definition, you can configure the internal error handling mechanism of a step.
### Skip Workflow on Step Failure
A step has a `skipOnPermanentFailure` configuration that allows you to configure what happens when an error occurs in the step. Its value can be a boolean or a string.
By default, `skipOnPermanentFailure` is disabled. When it's enabled, the workflow's status is set to `skipped` instead of `failed`. This means:
- Compensation functions of the workflow's steps are not called.
- The workflow's caller continues executing. You can still [access the error](#disable-error-throwing-in-workflow) that occurred during the workflow's execution as mentioned in the [Disable Error Throwing](#disable-error-throwing-in-workflow) section.
This is useful when you want to perform actions if no error occurs, but you don't care about compensating the workflow's steps or you don't want to stop the caller's execution.
You can think of setting the `skipOnPermanentFailure` to `true` as the equivalent of the following `try-catch` block:
```ts title="Outside a Workflow"
try {
actionThatThrowsError()
moreActions()
} catch (e) {
// don't do anything
}
```
You can do this in a workflow using the step's `skipOnPermanentFailure` configuration:
export const skipOnPermanentFailureEnabledHighlights = [
["13", "skipOnPermanentFailure", "Skip the rest of the workflow\nif an error occurs in the step."],
]
```ts title="Workflow Equivalent" highlights={skipOnPermanentFailureEnabledHighlights}
import {
createWorkflow
} from "@medusajs/framework/workflows-sdk"
import {
actionThatThrowsError,
moreActions
} from "./steps"
export const myWorkflow = createWorkflow(
"hello-world",
function (input) {
actionThatThrowsError().config({
skipOnPermanentFailure: true,
})
// This action will not be executed if the previous step throws an error
moreActions()
}
)
```
You set the configuration of a step by chaining the `config` method to the step's function call. The `config` method accepts an object similar to the one that can be passed to `createStep`.
In this example, if the `actionThatThrowsError` step throws an error, the rest of the workflow will be skipped, and the `moreActions` step will not be executed.
You can then access the error that occurred in that step as explained in the [Disable Error Throwing](#disable-error-throwing-in-workflow) section.
### Continue Workflow Execution from a Specific Step
In some cases, if an error occurs in a step, you may want to continue the workflow's execution from a specific step instead of stopping the workflow's execution or skipping the rest of the steps.
The `skipOnPermanentFailure` configuration can accept a step's ID as a value. Then, the workflow will continue execution from that step if an error occurs in the step that has the `skipOnPermanentFailure` configuration.
<Note>
The compensation function of the step that has the `skipOnPermanentFailure` configuration will not be called when an error occurs.
</Note>
You can think of setting the `skipOnPermanentFailure` to a step's ID as the equivalent of the following `try-catch` block:
```ts title="Outside a Workflow"
try {
actionThatThrowsError()
moreActions()
} catch (e) {
// do nothing
}
continueExecutionFromStep()
```
You can do this in a workflow using the step's `skipOnPermanentFailure` configuration:
export const skipOnPermanentFailureStepHighlights = [
["16", "skipOnPermanentFailure", "Continue the workflow's execution\nfrom a specific step if an error occurs."],
]
```ts title="Workflow Equivalent" highlights={skipOnPermanentFailureStepHighlights}
import {
createWorkflow
} from "@medusajs/framework/workflows-sdk"
import {
actionThatThrowsError,
moreActions,
continueExecutionFromStep
} from "./steps"
export const myWorkflow = createWorkflow(
"hello-world",
function (input) {
actionThatThrowsError().config({
// The `continue-execution-from-step` is the ID passed as a first
// parameter to `createStep` of `continueExecutionFromStep`.
skipOnPermanentFailure: "continue-execution-from-step",
})
// This action will not be executed if the previous step throws an error
moreActions()
// This action will be executed either way
continueExecutionFromStep()
}
)
```
In this example, you configure the `actionThatThrowsError` step to continue the workflow's execution from the `continueExecutionFromStep` step if an error occurs in the `actionThatThrowsError` step.
Notice that you pass the ID of the `continueExecutionFromStep` step as it's set in the `createStep` function.
So, the `moreActions` step will not be executed if the `actionThatThrowsError` step throws an error, and the `continueExecutionFromStep` will be executed anyway.
You can then access the error that occurred in that step as explained in the [Disable Error Throwing](#disable-error-throwing-in-workflow) section.
<Note>
If the specified step ID doesn't exist in the workflow, it will be equivalent to setting the `skipOnPermanentFailure` configuration to `true`. So, the workflow will be skipped, and the rest of the steps will not be executed.
</Note>
### Set Step as Failed, but Continue Workflow Execution
In some cases, you may want to fail a step, but continue the rest of the workflow's execution.
This is useful when you don't want a step's failure to stop the workflow's execution, but you want to mark that step as failed.
The `continueOnPermanentFailure` configuration allows you to do that. When enabled, the workflow's execution will continue, but the step will be marked as failed if an error occurs in that step.
<Note>
The compensation function of the step that has the `continueOnPermanentFailure` configuration will not be called when an error occurs.
</Note>
You can think of setting the `continueOnPermanentFailure` to `true` as the equivalent of the following `try-catch` block:
```ts title="Outside a Workflow"
try {
actionThatThrowsError()
} catch (e) {
// do nothing
}
moreActions()
```
You can do this in a workflow using the step's `continueOnPermanentFailure` configuration:
export const continueOnPermanentFailureHighlights = [
["13", "continueOnPermanentFailure", "Continue the workflow's execution\neven if an error occurs in the step."],
]
```ts title="Workflow Equivalent" highlights={continueOnPermanentFailureHighlights}
import {
createWorkflow
} from "@medusajs/framework/workflows-sdk"
import {
actionThatThrowsError,
moreActions
} from "./steps"
export const myWorkflow = createWorkflow(
"hello-world",
function (input) {
actionThatThrowsError().config({
continueOnPermanentFailure: true,
})
// This action will be executed even if the previous step throws an error
moreActions()
}
)
```
In this example, if the `actionThatThrowsError` step throws an error, the `moreActions` step will still be executed.
You can then access the error that occurred in that step as explained in the [Disable Error Throwing](#disable-error-throwing-in-workflow) section.

View File

@@ -1,10 +1,10 @@
export const metadata = {
title: `${pageNumber} Variable Manipulation in Workflows with transform`,
title: `${pageNumber} Data Manipulation in Workflows with transform`,
}
# {metadata.title}
In this chapter, you'll learn how to use `transform` from the Workflows SDK to manipulate variables in a workflow.
In this chapter, you'll learn how to use `transform` from the Workflows SDK to manipulate data and variables in a workflow.
## Why Variable Manipulation isn't Allowed in Workflows

View File

@@ -59,7 +59,7 @@ This workflow's executions fail if they run longer than two seconds.
<Note title="Tip">
A workflows timeout error is returned in the `errors` property of the workflows execution, as explained in [this chapter](../access-workflow-errors/page.mdx). The errors name is `TransactionTimeoutError`.
A workflows timeout error is returned in the `errors` property of the workflows execution, as explained in [this chapter](../errors/page.mdx). The errors name is `TransactionTimeoutError`.
</Note>
@@ -95,6 +95,6 @@ This step's executions fail if they run longer than two seconds.
<Note title="Tip">
A steps timeout error is returned in the `errors` property of the workflows execution, as explained in [this chapter](../access-workflow-errors/page.mdx). The errors name is `TransactionStepTimeoutError`.
A steps timeout error is returned in the `errors` property of the workflows execution, as explained in [this chapter](../errors/page.mdx). The errors name is `TransactionStepTimeoutError`.
</Note>

View File

@@ -10,12 +10,11 @@ export const generatedEditDates = {
"app/learn/storefront-development/page.mdx": "2024-12-10T09:11:04.993Z",
"app/learn/fundamentals/page.mdx": "2024-07-04T17:26:03+03:00",
"app/learn/fundamentals/admin-customizations/page.mdx": "2024-10-07T12:41:39.218Z",
"app/learn/fundamentals/workflows/workflow-timeout/page.mdx": "2024-10-21T13:30:21.372Z",
"app/learn/fundamentals/workflows/workflow-timeout/page.mdx": "2025-04-24T13:15:14.472Z",
"app/learn/fundamentals/workflows/parallel-steps/page.mdx": "2025-03-24T06:53:36.918Z",
"app/learn/fundamentals/medusa-container/page.mdx": "2024-12-09T11:02:38.225Z",
"app/learn/fundamentals/api-routes/page.mdx": "2024-12-04T11:02:57.134Z",
"app/learn/fundamentals/modules/modules-directory-structure/page.mdx": "2024-12-09T10:32:46.839Z",
"app/learn/fundamentals/workflows/access-workflow-errors/page.mdx": "2024-10-21T13:30:21.371Z",
"app/learn/fundamentals/events-and-subscribers/page.mdx": "2025-04-18T10:42:32.803Z",
"app/learn/fundamentals/modules/container/page.mdx": "2025-03-18T15:10:03.574Z",
"app/learn/fundamentals/workflows/execute-another-workflow/page.mdx": "2024-12-09T15:56:22.895Z",
@@ -32,13 +31,13 @@ export const generatedEditDates = {
"app/learn/fundamentals/modules/module-link-directions/page.mdx": "2024-07-24T09:16:01+02:00",
"app/learn/fundamentals/admin/page.mdx": "2025-04-18T10:28:47.328Z",
"app/learn/fundamentals/workflows/long-running-workflow/page.mdx": "2025-03-28T07:02:34.467Z",
"app/learn/fundamentals/workflows/constructor-constraints/page.mdx": "2025-02-12T13:55:33.437Z",
"app/learn/fundamentals/workflows/constructor-constraints/page.mdx": "2025-04-24T13:18:24.184Z",
"app/learn/fundamentals/data-models/write-migration/page.mdx": "2025-03-24T06:41:48.915Z",
"app/learn/fundamentals/data-models/manage-relationships/page.mdx": "2025-03-18T15:09:18.688Z",
"app/learn/fundamentals/modules/remote-query/page.mdx": "2024-07-21T21:20:24+02:00",
"app/learn/fundamentals/modules/options/page.mdx": "2025-03-18T15:12:34.510Z",
"app/learn/fundamentals/data-models/relationships/page.mdx": "2025-03-18T07:52:07.421Z",
"app/learn/fundamentals/workflows/compensation-function/page.mdx": "2024-12-06T14:34:50.384Z",
"app/learn/fundamentals/workflows/compensation-function/page.mdx": "2025-04-24T13:16:00.941Z",
"app/learn/fundamentals/modules/service-factory/page.mdx": "2025-03-18T15:14:13.486Z",
"app/learn/fundamentals/modules/module-links/page.mdx": "2024-09-30T08:43:53.126Z",
"app/learn/fundamentals/scheduled-jobs/execution-number/page.mdx": "2024-10-21T13:30:21.371Z",
@@ -73,7 +72,7 @@ export const generatedEditDates = {
"app/learn/fundamentals/modules/page.mdx": "2025-03-18T07:51:09.049Z",
"app/learn/debugging-and-testing/instrumentation/page.mdx": "2025-02-24T08:12:53.132Z",
"app/learn/fundamentals/api-routes/additional-data/page.mdx": "2025-04-17T08:50:17.036Z",
"app/learn/fundamentals/workflows/variable-manipulation/page.mdx": "2025-01-27T08:45:19.029Z",
"app/learn/fundamentals/workflows/variable-manipulation/page.mdx": "2025-04-24T13:14:43.967Z",
"app/learn/customization/custom-features/api-route/page.mdx": "2024-12-09T10:39:30.046Z",
"app/learn/customization/custom-features/module/page.mdx": "2025-03-18T07:49:30.590Z",
"app/learn/customization/custom-features/workflow/page.mdx": "2024-12-09T14:36:29.482Z",
@@ -119,5 +118,6 @@ export const generatedEditDates = {
"app/learn/fundamentals/module-links/read-only/page.mdx": "2025-04-18T11:09:13.328Z",
"app/learn/fundamentals/data-models/properties/page.mdx": "2025-03-18T07:57:17.826Z",
"app/learn/fundamentals/framework/page.mdx": "2025-04-17T16:07:19.090Z",
"app/learn/fundamentals/api-routes/retrieve-custom-links/page.mdx": "2025-04-18T07:38:56.729Z"
"app/learn/fundamentals/api-routes/retrieve-custom-links/page.mdx": "2025-04-18T07:38:56.729Z",
"app/learn/fundamentals/workflows/errors/page.mdx": "2025-04-24T14:47:13.368Z"
}

View File

@@ -668,12 +668,22 @@ export const generatedSidebars = [
"loaded": true,
"isPathHref": true,
"type": "link",
"path": "/learn/fundamentals/workflows/variable-manipulation",
"title": "Transform Variables",
"path": "/learn/fundamentals/workflows/compensation-function",
"title": "Compensation Function",
"children": [],
"chapterTitle": "3.7.2. Transform Variables",
"chapterTitle": "3.7.2. Compensation Function",
"number": "3.7.2."
},
{
"loaded": true,
"isPathHref": true,
"type": "link",
"path": "/learn/fundamentals/workflows/variable-manipulation",
"title": "Transform Data",
"children": [],
"chapterTitle": "3.7.3. Transform Data",
"number": "3.7.3."
},
{
"loaded": true,
"isPathHref": true,
@@ -681,18 +691,18 @@ export const generatedSidebars = [
"path": "/learn/fundamentals/workflows/conditions",
"title": "When-Then Conditions",
"children": [],
"chapterTitle": "3.7.3. When-Then Conditions",
"number": "3.7.3."
"chapterTitle": "3.7.4. When-Then Conditions",
"number": "3.7.4."
},
{
"loaded": true,
"isPathHref": true,
"type": "link",
"path": "/learn/fundamentals/workflows/compensation-function",
"title": "Compensation Function",
"path": "/learn/fundamentals/workflows/errors",
"title": "Error Handling",
"children": [],
"chapterTitle": "3.7.4. Compensation Function",
"number": "3.7.4."
"chapterTitle": "3.7.5. Error Handling",
"number": "3.7.5."
},
{
"loaded": true,
@@ -701,8 +711,8 @@ export const generatedSidebars = [
"path": "/learn/fundamentals/workflows/workflow-hooks",
"title": "Workflow Hooks",
"children": [],
"chapterTitle": "3.7.5. Workflow Hooks",
"number": "3.7.5."
"chapterTitle": "3.7.6. Workflow Hooks",
"number": "3.7.6."
},
{
"loaded": true,
@@ -711,17 +721,7 @@ export const generatedSidebars = [
"path": "/learn/fundamentals/workflows/add-workflow-hook",
"title": "Expose a Hook",
"children": [],
"chapterTitle": "3.7.6. Expose a Hook",
"number": "3.7.6."
},
{
"loaded": true,
"isPathHref": true,
"type": "link",
"path": "/learn/fundamentals/workflows/access-workflow-errors",
"title": "Access Workflow Errors",
"children": [],
"chapterTitle": "3.7.7. Access Workflow Errors",
"chapterTitle": "3.7.7. Expose a Hook",
"number": "3.7.7."
},
{

File diff suppressed because it is too large Load Diff

View File

@@ -351,10 +351,15 @@ export const sidebars = [
path: "/learn/fundamentals/workflows/constructor-constraints",
title: "Constructor Constraints",
},
{
type: "link",
path: "/learn/fundamentals/workflows/compensation-function",
title: "Compensation Function",
},
{
type: "link",
path: "/learn/fundamentals/workflows/variable-manipulation",
title: "Transform Variables",
title: "Transform Data",
},
{
type: "link",
@@ -363,8 +368,8 @@ export const sidebars = [
},
{
type: "link",
path: "/learn/fundamentals/workflows/compensation-function",
title: "Compensation Function",
path: "/learn/fundamentals/workflows/errors",
title: "Error Handling",
},
{
type: "link",
@@ -376,11 +381,6 @@ export const sidebars = [
path: "/learn/fundamentals/workflows/add-workflow-hook",
title: "Expose a Hook",
},
{
type: "link",
path: "/learn/fundamentals/workflows/access-workflow-errors",
title: "Access Workflow Errors",
},
{
type: "link",
path: "/learn/fundamentals/workflows/retry-failed-steps",

View File

@@ -364,6 +364,11 @@ const redirects = async () => {
destination: "/user-guide/settings/locations-and-shipping/locations",
permanent: true,
},
{
source: "/learn/fundamentals/workflows/access-workflow-errors",
destination: "/learn/fundamentals/workflows/errors",
permanent: true,
},
]
}