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:

ExceptionThrown byTrigger
ProviderExceptionProvidersUpstream call failed
AuthExceptionProviders, ClientMissing API key
InvalidArgumentExceptionResolver, PanelContextMissing or unknown provider config; a mistyped option in debug mode

ProviderException

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

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

providerName
ProviderName
The provider that produced the error.
model
String | null
The model id used for the request, or null when the failure happened before model resolution.
responseId
String | null
Provider-specific response identifier (OpenAI's response.id, Anthropic's message.id).
responseExcerpt
String | null
First 200 characters of the response body, single-line, for log triage.

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:

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

Example:

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

Catching

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.

TriggerMessage
Provider called without apiKey setMissing API key in "johannschopplich.copilot.providers.<name>.apiKey"
Client::requireApiKey() preflight checkSame message – preflight before kicking off long-running batch ops
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.

TriggerMessage
Missing provider configMissing required option "johannschopplich.copilot.provider"
Unknown provider nameUnknown provider "<name>" – set "johannschopplich.copilot.provider" to one of: openai, anthropic, google, mistral

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.

TriggerMessage
Option has the wrong typeInvalid <path>: expected <types>, got <type>
Option is outside its set of valid valuesInvalid <path>: <value>. Must be one of: <values>
Provider keys differing only in caseConflicting provider keys: <names> – provider names are case-insensitive
For the full configuration reference, see Global Configuration.