---
name: exceptions-25ac3b5ccdad
description: >-
  Cria exceções customizadas renderable e reportable para HTTP e logging em Laravel. Use quando precisar criar exception handlers, exceções de domínio, ou melhorar o tratamento de erros.
compatibility: PHP 8.2+, Laravel 11+
metadata:
  author: aronpc
  version: 5.0.0
  category: laravel
allowed-tools:
  - Read
  - Write
  - Edit
  - Bash
---

# exceptions

## Resumo
Cria exceções customizadas com interfaces renderable/reportable para HTTP responses e logging.

## Skills Relacionadas

| Skill | Quando usar junto |
|-------|-------------------|
| `architecture` | Para exceções de domínio na arquitetura |
| `actions` | Para tratamento em Actions |
| `testing` | Para testar exceções |
| `qa` | Para verificar tratamento de erros |

## Quando usar

Use esta skill sempre que:
- Criar regras de negócio que violam limites
- Criar validações de domínio específicas
- Implementar autorização customizada
- Implementar erros de pagamento/billing
- Criar exceções com contexto para logging/monitoramento

## Princípios Fundamentais

**CRÍTICO:** Use exceções especializadas para erros de domínio específicos - NUNCA use `Exception` genérico.

- **Domain Exceptions** - Crie exceções customizadas para violações de regras de negócio
- **HTTP Status Mapping** - Mapeie exceções para códigos HTTP apropriados
- **Renderable** - Implemente método `render()` para respostas HTTP customizadas
- **Reportable** - Implemente método `report()` para logging/monitoramento
- **Localized Messages** - Use `__()` para mensagens de exceção (EN, ES, PT-BR)

## Estrutura de Exception

> Ajuste os subdiretórios aos domínios do seu projeto. A árvore abaixo usa placeholders
> genéricos (`Domain1/`, `Domain2/`); os exemplos de código nas seções seguintes usam um
> domínio ilustrativo (`Business`, `Payment`) apenas para demonstração.

```
app/
├── Exceptions/
│   ├── Domain1/
│   │   ├── ResourceLimitExceededException.php
│   │   ├── ResourceNotFoundException.php
│   │   └── InvalidResourceTypeException.php
│   └── Domain2/
│       ├── InsufficientCreditsException.php
│       ├── PlanLimitExceededException.php
│       └── PaymentFailedException.php
```

## Criando Exceções Customizadas

```bash
php artisan make:exception Business/BusinessLimitExceededException
```

## Padrão de Exception

```php
<?php

declare(strict_types=1);

namespace App\Exceptions\Business;

use Exception;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;

final class BusinessLimitExceededException extends Exception
{
    /**
     * Create a new exception instance.
     */
    public function __construct(
        public readonly int $currentCount,
        public readonly int $maxAllowed,
        public readonly string $planName,
    ) {
        parent::__construct(
            message: __('exceptions.business_limit_exceeded', [
                'current' => $this->currentCount,
                'max' => $this->maxAllowed,
                'plan' => $this->planName,
            ]),
            code: 403
        );
    }

    /**
     * Render exception as an HTTP response.
     */
    public function render(Request $request): JsonResponse|RedirectResponse
    {
        if ($request->expectsJson()) {
            return response()->json([
                'message' => $this->getMessage(),
                'current_count' => $this->currentCount,
                'max_allowed' => $this->maxAllowed,
                'plan_name' => $this->planName,
            ], 403);
        }

        return back()->with('error', $this->getMessage());
    }

    /**
     * Report exception (logging/monitoramento).
     */
    public function report(): void
    {
        // Log or send to monitoring service
        Log::warning('Business limit exceeded', [
            'current' => $this->currentCount,
            'max' => $this->maxAllowed,
            'plan' => $this->planName,
        ]);
    }
}
```

## Uso em Actions

```php
<?php

declare(strict_types=1);

namespace App\Actions\Business;

use App\DataObjects\Business\CreateBusinessData;
use App\Exceptions\Business\BusinessLimitExceededException;
use App\Models\Business;
use App\Models\Tenant;
use Lorisleiva\Actions\Concerns\AsAction;

final class CreateBusinessAction
{
    use AsAction;

    public function handle(Tenant $tenant, CreateBusinessData $data): Business
    {
        // Check business limit
        $currentCount = $tenant->businesses()->count();
        $maxAllowed = $tenant->plan->limits['businesses'] ?? 0;

        if ($currentCount >= $maxAllowed) {
            throw new BusinessLimitExceededException(
                currentCount: $currentCount,
                maxAllowed: $maxAllowed,
                planName: $tenant->plan->name,
            );
        }

        // Create business
        return $tenant->businesses()->create($data->toArray());
    }
}
```

## Tipos Comuns de Exception

### Exceções de Domínio/Regra de Negócio

```php
// When resource not found
throw new BusinessNotFoundException($id);

// When validation fails
throw new InvalidBusinessTypeException($type);

// When limit exceeded
throw new BusinessLimitExceededException($current, $max, $plan);
```

### Exceções de Autorização

```php
// When tenant access denied
throw new UnauthorizedTenantAccessException($tenantId);

// When tenant is inactive
throw new TenantInactiveException($tenant);

// When tenant is suspended
throw new TenantSuspendedException($tenant, $reason);
```

### Exceções de Pagamento/Billing

```php
// When credits insufficient
throw new InsufficientCreditsException($required, $available);

// When plan limit exceeded
throw new PlanLimitExceededException($resource, $limit);

// When payment fails
throw new PaymentFailedException($transactionId, $reason);
```

## Convenção de Nomenclatura de Exceções

| Tipo | Convenção | Exemplo |
|------|----------|---------|
| **Not Found** | `*NotFoundException` | `BusinessNotFoundException` |
| **Invalid Input** | `Invalid*Exception` | `InvalidBusinessTypeException` |
| **Limit Exceeded** | `*LimitExceededException` | `BusinessLimitExceededException` |
| **Unauthorized** | `Unauthorized*Exception` | `UnauthorizedTenantAccessException` |
| **Payment** | `Payment*Exception` | `PaymentFailedException` |
| **Resource State** | `*InactiveException` | `TenantInactiveException` |
| **Business Rule** | Nome descritivo | `MenuItemUnavailableException` |

## Arquivos de Tradução

```php
// lang/en/exceptions.php
return [
    'business_limit_exceeded' => 'Business limit exceeded. You have :current businesses, but your :plan plan allows only :max.',
    'business_not_found' => 'Business not found.',
    'invalid_business_type' => 'Invalid business type: :type',
    'tenant_inactive' => 'Your account is inactive. Please contact support.',
    'tenant_suspended' => 'Your account has been suspended. Reason: :reason',
    'insufficient_credits' => 'Insufficient credits. Required: :required, Available: :available',
];

// lang/es/exceptions.php
return [
    'business_limit_exceeded' => 'Límite de negocios excedido. Tienes :current negocios, pero tu plan :plan permite solo :max.',
    // ...
];

// lang/pt_BR/exceptions.php
return [
    'business_limit_exceeded' => 'Limite de negócios excedido. Você tem :current negócios, mas seu plano :plan permite apenas :max.',
    // ...
];
```

## Mapeamento de Código HTTP

| Exception Type | HTTP Status | Descrição |
|----------------|-------------|-------------|
| **NotFoundException** | 404 | Resource not found |
| **Unauthorized** | 403 | Access denied |
| **ValidationException** | 422 | Validation failed |
| **LimitExceeded** | 403 | Resource limit exceeded |
| **PaymentFailed** | 402 | Payment required |
| **InactiveException** | 403 | Account/resource inactive |
| **SuspendedException** | 403 | Account suspended |

## Testando Exceções

```php
<?php

use App\Actions\Business\CreateBusinessAction;
use App\Exceptions\Business\BusinessLimitExceededException;

it('throws exception when business limit exceeded', function () {
    $tenant = Tenant::factory()->create(['plan_id' => 1]);
    $tenant->plan->update(['limits' => ['businesses' => 2]]);

    // Create 2 businesses (at limit)
    Business::factory()->count(2)->create(['tenant_id' => $tenant->id]);

    // Try to create 3rd business
    $data = CreateBusinessData::fromArray([
        'name' => 'New Business',
        'type' => 'restaurant',
    ]);

    expect(fn() => CreateBusinessAction::run($tenant, $data))
        ->toThrow(BusinessLimitExceededException::class, 'Business limit exceeded');
});

it('includes exception context in response', function () {
    $tenant = Tenant::factory()->create(['plan_id' => 1]);
    Business::factory()->count(2)->create(['tenant_id' => $tenant->id]);

    try {
        CreateBusinessAction::run($tenant, $data);
    } catch (BusinessLimitExceededException $e) {
        expect($e->currentCount)->toBe(2)
            ->and($e->maxAllowed)->toBe(2)
            ->and($e->planName)->toBe('Basic');
    }
});
```

## Global Exception Handler

No Laravel 11+ não existe mais o `app/Exceptions/Handler.php`. Toda a
configuração de exceções vive em `bootstrap/app.php`, no callback
`->withExceptions(...)`.

```php
<?php

use App\Exceptions\Business\BusinessLimitExceededException;
use Illuminate\Foundation\Application;
use Illuminate\Foundation\Configuration\Exceptions;
use Illuminate\Http\Request;
use Psr\Log\LogLevel;
use Throwable;

return Application::configure(basePath: dirname(__DIR__))
    ->withExceptions(function (Exceptions $exceptions): void {
        // Callback de report (substitui o antigo register()/reportable)
        $exceptions->report(function (Throwable $e): void {
            // Send to monitoring service (Sentry, Bugsnag, etc.)
        });

        // Resposta HTTP customizada por tipo de exceção
        $exceptions->render(function (BusinessLimitExceededException $e, Request $request) {
            if ($request->expectsJson()) {
                return response()->json(['message' => $e->getMessage()], 403);
            }

            return back()->with('error', $e->getMessage());
        });

        // Exceções que não devem ser reportadas (substitui $dontReport)
        $exceptions->dontReport([
            BusinessLimitExceededException::class,
        ]);

        // Nível de log customizado por tipo (substitui $levels)
        $exceptions->level(BusinessLimitExceededException::class, LogLevel::WARNING);
    })->create();
```

## Melhores Práticas

### ✅ FAÇA

- Crie exceções de domínio específicas para cada violação de regra de negócio
- Inclua dados de contexto no construtor da exceção
- Implemente `render()` para respostas HTTP customizadas
- Implemente `report()` para logging/monitoramento
- Use chaves de tradução para mensagens
- Teste exceções sendo lançadas nas Actions
- Mapeie exceções para códigos HTTP apropriados

### ❌ NÃO FAÇA

- NÃO coloque lógica de negócio nas classes de exceção
- NÃO catch exceções apenas para relançar
- NÃO esqueça de traduzir mensagens de erro
- NÃO use `Exception` genérico sem motivo claro
- NÃO crie exceções para cada regra de validação (use Form Requests)
- NÃO esqueça de testar comportamento de exceções

## Quando Criar Exceções Customizadas

✅ Violações de regras de negócio (limits, constraints) \
✅ Erros de domínio específicos (invalid state transitions) \
✅ Falhas de autorização (tenant access, ownership) \
✅ Erros de pagamento/billing \
✅ Resource not found (quando 404 genérico não é suficiente)

❌ Erros de validação (use Form Requests) \
❌ Erros de conexão de banco (framework handles) \
❌ Erros genéricos (use built-in exceptions)

## Referências Cruzadas

- **Traduções**: Veja `i18n` para traduções de mensagens de erro em EN, ES, PT-BR
- **Actions**: Usado em Actions (veja `architecture` e `actions`)
- **Testes**: Veja `testing` para testar exceções sendo lançadas

## Referências

- [Laravel Error Handling](https://laravel.com/docs/errors) - Documentação oficial


---
Source attribution: aronpc
Original project: https://github.com/aronpc/ai/blob/85e01f1cbbd8159dd87c408f226435c0b3201c09/skills/exceptions/SKILL.md
License: MIT
License information: https://opensource.org/license/mit
Original upstream instructions; downloaded from Mega AI Hub.

## Upstream license notice

MIT License

Copyright (c) 2026 AronPC

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.



# Mega AI Hub resource metadata

Creator / publisher: aronpc
Original project: https://github.com/aronpc/ai/blob/85e01f1cbbd8159dd87c408f226435c0b3201c09/skills/exceptions/SKILL.md
Attribution basis: Source repository record

Fields of work: Development & Engineering, Product Management
Roles: Backend Developer, Full-Stack Developer, Product Manager
Specialties: Backend, Cross Platform
Tools: Laravel
Country applicability: Country not specified
Classification basis: Source category, role and specialization labels
Instruction SHA-256: d6bccce243373563c19b5d7da4ff30dd85ba14cd133f133d15619bf48625cebf
