# Source: https://upstash.com/docs/workflow/features/retries/prevent-retries.md

> ## Documentation Index
> Fetch the complete documentation index at: https://upstash.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Prevent Retries

It is recommended to enable retries for workflow runs to improve reliability.

However, in some cases, you may want to stop execution immediately when an error occurs, without causing additional retries.
Upstash Workflow provides several mechanisms to terminate workflow execution gracefully.

## Using `WorkflowNonRetryableError`

`WorkflowNonRetryableError` lets you explicitly fail a workflow without entering the retry cycle.

When thrown, the workflow run is marked as failed, which:

* Triggers the failure function (if defined)
* Sends the workflow run to the DLQ

```ts TypeScript highlight={7} theme={"system"}
export const { POST } = serve<{ topic: string }>(async (context) => {
  const payload = context.requestPayload

  const isExists = await context.run("is-user-exists", () => { ... });

  if (!isExists) {
    throw new WorkflowNonRetryableError("The user does not exists!")
  }
})
```

## Using `context.cancel()`

You can cancel a workflow run explicitly from inside the workflow.

When canceled, the run is labeled as canceled instead of failed. This means:

* The failure handler will **NOT** be triggered
* The workflow will **NOT** be sent to the DLQ

<CodeGroup>
  ```typescript highlight={10-11} TypeScript theme={"system"}
  export const { POST } = serve<{ orderId: string }>(async (context) => {
    const { orderId } = context.requestPayload;

    // Check if order is still valid
    const orderStatus = await context.run("check-order-status", async () => {
      return await getOrderStatus(orderId);
    });

    if (orderStatus === "cancelled") {
      // Stop execution gracefully without error
      await context.cancel();
      return;
    }

    // Continue processing if order is valid
    await context.run("process-order", async () => {
      return await processOrder(orderId);
    });
  });
  ```

  ```python Python theme={"system"}
  @serve.post("/graceful-cancellation")
  async def graceful_cancellation(context: AsyncWorkflowContext[dict]) -> None:
      order_id = context.request_payload["order_id"]

      async def _check_order_status():
          return await get_order_status(order_id)

      # Check if order is still valid
      order_status = await context.run("check-order-status", _check_order_status)

      if order_status == "cancelled":
          # Stop execution gracefully without error
          await context.cancel()
          return

      # Continue processing if order is valid
      async def _process_order():
          return await process_order(order_id)

      await context.run("process-order", _process_order)
  ```
</CodeGroup>

## Using conditional execution

You can also use guard conditions to skip certain steps and exit early, without throwing errors or canceling the workflow.

In this case, the workflow run completes successfully because no error was raised.

<CodeGroup>
  ```typescript TypeScript highlight={10-11} theme={"system"}
  export const { POST } = serve<{ data: any }>(async (context) => {
    const { data } = context.requestPayload;

    // Check if order is still valid
    const orderStatus = await context.run("check-order-status", async () => {
      return await getOrderStatus(orderId);
    });

    if (orderStatus === "not-found") {
      // Stop execution without error
      return;
    }

    // Continue processing if order is valid
    await context.run("process-order", async () => {
      return await processOrder(orderId);
    });
  });
  ```

  ```python Python theme={"system"}
  @serve.post("/conditional-execution")
  async def conditional_execution(context: AsyncWorkflowContext[dict]) -> None:
      data = context.request_payload["data"]

      async def _validate_data():
          return validate_input_data(data)

      # Validate data first
      validation_result = await context.run("validate-data", _validate_data)

      if not validation_result["is_valid"]:
          # Log the validation failure
          async def _log_validation_failure():
              await log_validation_error(validation_result["errors"])

          await context.run("log-validation-failure", _log_validation_failure)

          # Stop execution without error
          return

      # Only execute if validation passes
      async def _process_valid_data():
          return await process_data(data)

      await context.run("process-valid-data", _process_valid_data)
  ```
</CodeGroup>