---
title: "Exceptions"
description: "Catch upstream provider failures, missing API keys, and configuration errors with three typed exceptions."
canonical_url: "https://kirby.tools/docs/copilot/php-classes/exceptions"
---

# Exceptions

> Catch upstream provider failures, missing API keys, and configuration errors with three typed exceptions.

All three extend `Kirby\Exception\Exception`. Only `ProviderException` carries a structured `details` payload; the other two are raised with a message alone:

<table>
<thead>
  <tr>
    <th>
      Exception
    </th>
    
    <th>
      Thrown by
    </th>
    
    <th>
      Trigger
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        ProviderException
      </code>
    </td>
    
    <td>
      Providers
    </td>
    
    <td>
      Upstream call failed
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        AuthException
      </code>
    </td>
    
    <td>
      Providers, <code>
        Client
      </code>
    </td>
    
    <td>
      Missing API key
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        InvalidArgumentException
      </code>
    </td>
    
    <td>
      <code>
        Resolver
      </code>
      
      , <code>
        PanelContext
      </code>
    </td>
    
    <td>
      Missing or unknown <code>
        provider
      </code>
      
       config; a mistyped option in debug mode
    </td>
  </tr>
</tbody>
</table>

## `ProviderException`

`JohannSchopplich\Copilot\AI\Exception\ProviderException` extends `Kirby\Exception\Exception`. Thrown when an upstream AI provider call fails.

```php
final class ProviderException extends \Kirby\Exception\Exception
{
    protected static string $defaultKey = 'copilot.ai.provider';
    protected static int $defaultHttpCode = 502;

    public function __construct(
        ProviderName $providerName,
        string $reason,
        string|null $model = null,
        string|null $responseId = null,
        string|null $responseExcerpt = null,
        int|null $httpCode = null,
        Throwable|null $previous = null,
    );
}
```

### Details Payload

<field-group>
<field name="providerName" type="ProviderName">

The provider that produced the error.

</field>

<field name="model" type="String | null">

The model id used for the request, or `null` when the failure happened before model resolution.

</field>

<field name="responseId" type="String | null">

Provider-specific response identifier (OpenAI's `response.id`, Anthropic's `message.id`).

</field>

<field name="responseExcerpt" type="String | null">

First 200 characters of the response body, single-line, for log triage.

</field>
</field-group>

The original upstream error is attached as `previous`. The HTTP status (when available) is on the `httpCode` constructor argument and accessible via `getHttpCode()`.

### Message Format

The exception message is built from the constructor arguments:

```text
<provider> provider error: <reason> (model: <model>, request: <responseId>, response: <excerpt>)
```

Example:

```text
openai provider error: request failed: Rate limit reached (model: gpt-5.6-luna, request: resp_abc123, response: Rate limit exceeded for organization …)
```

### Catching

```php
use JohannSchopplich\Copilot\AI\Client;
use JohannSchopplich\Copilot\AI\Exception\ProviderException;

try {
    $result = Client::instance()->generateObject($messages, $schema);
} catch (ProviderException $error) {
    $details = $error->getDetails();
    // [
    //   'providerName' => ProviderName::OpenAI,
    //   'model' => 'gpt-5.6-luna',
    //   'responseId' => 'resp_abc123',
    //   'responseExcerpt' => 'Rate limit exceeded …',
    // ]

    $http = $error->getHttpCode(); // 429
    $upstream = $error->getPrevious(); // OpenAI\Exceptions\RateLimitException
}
```

The default HTTP code 502 applies whenever the failure carries no upstream status – an unparsable response, for example.

## `AuthException`

`Kirby\Exception\AuthException` is thrown when an API key is missing.

<table>
<thead>
  <tr>
    <th>
      Trigger
    </th>
    
    <th>
      Message
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      Provider called without <code>
        apiKey
      </code>
      
       set
    </td>
    
    <td>
      <code>
        Missing API key in "johannschopplich.copilot.providers.<name>.apiKey"
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        Client::requireApiKey()
      </code>
      
       preflight check
    </td>
    
    <td>
      Same message – preflight before kicking off long-running batch ops
    </td>
  </tr>
</tbody>
</table>

```php
use JohannSchopplich\Copilot\AI\Client;
use Kirby\Exception\AuthException;

try {
    $client = Client::instance();
    $client->requireApiKey();
} catch (AuthException $error) {
    // Show a config hint to the user
}
```

## `InvalidArgumentException`

`Kirby\Exception\InvalidArgumentException` is thrown by `Resolver::fromKirbyOptions()` for misconfiguration.

<table>
<thead>
  <tr>
    <th>
      Trigger
    </th>
    
    <th>
      Message
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      Missing <code>
        provider
      </code>
      
       config
    </td>
    
    <td>
      <code>
        Missing required option "johannschopplich.copilot.provider"
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      Unknown provider name
    </td>
    
    <td>
      <code>
        Unknown provider "<name>" – set "johannschopplich.copilot.provider" to one of: openai, anthropic, google, mistral
      </code>
    </td>
  </tr>
</tbody>
</table>

### Config Shape Errors

While building the Panel's context, Copilot also checks the shape of every option it reads. These checks raise `InvalidArgumentException` **only when Kirby's debug option is on**; with debug off, the offending option falls back to its default so that one typo can't take the whole Panel down.

<table>
<thead>
  <tr>
    <th>
      Trigger
    </th>
    
    <th>
      Message
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      Option has the wrong type
    </td>
    
    <td>
      <code>
        Invalid <path>: expected <types>, got <type>
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      Option is outside its set of valid values
    </td>
    
    <td>
      <code>
        Invalid <path>: <value>. Must be one of: <values>
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      Provider keys differing only in case
    </td>
    
    <td>
      <code>
        Conflicting provider keys: <names> – provider names are case-insensitive
      </code>
    </td>
  </tr>
</tbody>
</table>

<callout color="info" icon="i-ri-arrow-right-line" to="/docs/copilot/configuration/global">

For the full configuration reference, see **Global Configuration**.

</callout>

---

Every page of this site as Markdown: <https://kirby.tools/sitemap.md>
