> For the complete documentation index, see [llms.txt](https://docs.skail.dev/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.skail.dev/construir/escrever-fluxos/como-tratar-erros-e-compensar.md).

# Como tratar erros e compensar

Onde o `try/catch` entra em uma function, o que fazer com uma exceção que esgotou as tentativas, como desfazer passos anteriores com compensações, e o que nunca capturar.

## O que chega à function

Um command que lança exceção é retentado pelo runtime até o `retryCount` (5 por padrão). A function não vê essas tentativas. Só quando esgotam, a exceção é lançada no `await` do command, como uma exceção comum de `async/await`. É aí que o `try/catch` entra; um laço de retry manual dentro da function duplica o que o runtime já fez. Ver [Falhas, retries e idempotência](/aprender/fundamentos/falhas-retries-e-idempotencia.md).

Antes de escrever `catch`, separe dois tipos de falha. Falha técnica (timeout, 5xx, conexão recusada): exceção, retentada pelo runtime, pode dar certo na próxima. Falha de negócio (cartão recusado, estoque insuficiente, CPF inválido): não é exceção, é um resultado; repetir não muda nada. Commands devolvem falhas de negócio como valor, e a function decide.

## O padrão: reservar, cobrar, confirmar, com compensação

```csharp
public enum ResultadoCobranca { Aprovado, Recusado }

[SkailFunction]
public async SkailTask FecharPedido(Guid pedidoId)
{
    await ReservarEstoque(pedidoId);                                  // passo 1

    ResultadoCobranca cobranca;
    try
    {
        cobranca = await CobrarCartao(pedidoId);                      // passo 2; exceção só após 8 tentativas
    }
    catch (HttpRequestException ex)
    {
        await LiberarEstoque(pedidoId);                               // compensa o passo 1
        await MarcarPedidoComoPendente(pedidoId, $"gateway indisponível: {ex.Message}");
        return;
    }

    if (cobranca == ResultadoCobranca.Recusado)                       // falha de negócio: resultado
    {
        await LiberarEstoque(pedidoId);
        await NotificarRecusa(pedidoId);
        return;
    }

    try
    {
        await EmitirNotaFiscal(pedidoId);                             // passo 3
    }
    catch (HttpRequestException)
    {
        await EstornarCobranca(pedidoId);                             // compensa o passo 2
        await LiberarEstoque(pedidoId);                               // compensa o passo 1
        await MarcarPedidoComoPendente(pedidoId, "emissão indisponível");
        return;
    }

    await ConfirmarPedido(pedidoId);
}

[SkailCommand(retryCount: 8)]
public async SkailTask<ResultadoCobranca> CobrarCartao(Guid pedidoId) { /* gateway, com Idempotency-Key */ }

[SkailCommand(retryCount: 8)]
public async SkailTask EstornarCobranca(Guid pedidoId) { /* gateway, com Idempotency-Key */ }

[SkailCommand]
public async SkailTask LiberarEstoque(Guid pedidoId) { /* idempotente: liberar duas vezes não faz mal */ }
```

O que está garantido aqui. Cada compensação é um command, então ela também é retentada e memoizada: se a function for retomada no meio das compensações, as que já rodaram não repetem. Se `EstornarCobranca` esgotar as próprias tentativas, a exceção sai da function, que é reentregue até 15 vezes; quando esgotar, a execução fica como falha no Monitor, com o pedido em estado conhecido (cobrado, sem nota), e pode ser retomada manualmente depois que o gateway voltar.

## Compensações em ordem inversa

Com mais passos, mantenha uma lista do que precisa ser desfeito e execute ao contrário. A lista vive em uma variável local da function: ela é reconstruída no replay a partir dos mesmos resultados, então é determinística.

```csharp
var compensacoes = new List<Func<SkailTask>>();
try
{
    await ReservarEstoque(pedidoId);      compensacoes.Add(() => LiberarEstoque(pedidoId));
    await CobrarCartao(pedidoId);         compensacoes.Add(() => EstornarCobranca(pedidoId));
    await EmitirNotaFiscal(pedidoId);     compensacoes.Add(() => CancelarNota(pedidoId));
    await AgendarEntrega(pedidoId);
}
catch (Exception ex) when (ex is not SkailNonDeterministicException)
{
    for (var i = compensacoes.Count - 1; i >= 0; i--)
        await compensacoes[i]();
    await MarcarPedidoComoPendente(pedidoId, ex.Message);
}
```

## O que nunca capturar

`SkailNonDeterministicException`: indica que o código e o histórico divergem; capturar esconde o problema e corrompe o fluxo. `SkailException` genérica com `catch (Exception)` sem filtro: você pode engolir um erro de infraestrutura que o runtime precisa ver. Use `when` para excluir as exceções do skail, como no exemplo.

## Quando não compensar

Nem tudo precisa ser desfeito. Se a falha é em um passo sem efeito lateral relevante (registrar um log de auditoria), marcar pendente e seguir pode ser melhor que reverter uma cobrança. A decisão é do negócio; o que o skail dá é a garantia de que, decidido, o caminho executa até o fim ou fica visível no Monitor.

## Erros comuns

| Sintoma                            | Causa                                        | Correção                                                                                                 |
| ---------------------------------- | -------------------------------------------- | -------------------------------------------------------------------------------------------------------- |
| Recusa de cartão retentada 8 vezes | Falha de negócio lançada como exceção        | Devolva `ResultadoCobranca.Recusado`                                                                     |
| Compensação executada duas vezes   | Compensação com I/O direto na function       | Compensação como command idempotente                                                                     |
| `catch` nunca entra                | Esperava ver cada tentativa                  | O runtime retenta antes; a function só vê a última                                                       |
| Estorno duplicado                  | Command de estorno sem chave de idempotência | [Como escrever commands idempotentes](/construir/escrever-fluxos/como-escrever-commands-idempotentes.md) |

## Próximos passos

[Saga](/construir/padroes-e-antipadroes/saga.md) é este padrão com mais passos e um cenário completo. [Retomada manual de execuções falhas](/operar/retomada-manual-de-execucoes-falhas.md) para o que fazer quando a compensação também falha. A referência de [Exceções](/construir/sdk-.net/excecoes.md).
