Prism Vertex provides comprehensive error handling for all API requests. When a request fails, the package throws specific exception types that help you identify and handle different error conditions.
Exception hierarchy
All Vertex AI errors extend from Prism’s base exception classes:
Illuminate\Http\Client\RequestException
└── Prism\Prism\Exceptions\PrismException
├── Prism\Prism\Exceptions\PrismRequestTooLargeException
├── Prism\Prism\Exceptions\PrismRateLimitedException
└── Prism\Prism\Exceptions\PrismProviderOverloadedException
HTTP status code mapping
Prism Vertex maps HTTP status codes to specific exception types:
| Status Code | Exception | Description |
|---|
| 413 | PrismRequestTooLargeException | Request payload exceeds size limit |
| 429 | PrismRateLimitedException | Rate limit exceeded |
| 503 | PrismProviderOverloadedException | Provider is temporarily overloaded |
| 529 | PrismProviderOverloadedException | Provider is temporarily overloaded |
| Other | PrismException | General error with details |
Implementation
The status code mapping is handled by Vertex::handleRequestException():
public function handleRequestException(string $model, RequestException $e): never
{
match ($e->response->getStatusCode()) {
413 => throw PrismRequestTooLargeException::make(class_basename($this)),
429 => throw PrismRateLimitedException::make([]),
503, 529 => throw PrismProviderOverloadedException::make(class_basename($this)),
default => $this->handleResponseErrors($e),
};
}
Vertex AI returns errors in multiple formats depending on the provider and schema. Prism Vertex normalizes these into a consistent exception format.
Standard Google API errors
Most Google Vertex AI errors follow the google.rpc.Status format:
{
"error": {
"code": 400,
"message": "Invalid argument",
"status": "INVALID_ARGUMENT"
}
}
Anthropic partner errors
Anthropic models via :rawPredict return errors in Anthropic’s format:
{
"type": "error",
"error": {
"type": "invalid_request_error",
"message": "messages: field required"
}
}
OpenAI-compatible errors
OpenAI-schema models return errors similar to the OpenAI API:
{
"error": {
"type": "invalid_request_error",
"message": "Invalid model specified"
}
}
Array-wrapped errors
Some endpoints wrap errors in an array:
[
{
"error": {
"code": 400,
"message": "Bad request"
}
}
]
Field violation details
Google API errors may include detailed field violations:
{
"error": {
"code": 400,
"message": "Invalid request",
"status": "INVALID_ARGUMENT",
"details": [
{
"@type": "type.googleapis.com/google.rpc.BadRequest",
"fieldViolations": [
{
"field": "messages[0].content",
"description": "content must not be empty"
}
]
}
]
}
}
Prism Vertex uses Vertex::extractError() to normalize all error formats:
public static function extractError(?array $data, ?string $rawBody = null): array
{
if ($data === null || $data === []) {
return [null, $rawBody ?: null];
}
// Array-wrapped response: [{"error": {...}}]
if (array_is_list($data) && isset($data[0])) {
$data = $data[0];
}
// Anthropic partner model errors: {"type": "error", "error": {...}}
if (data_get($data, 'type') === 'error') {
return [
data_get($data, 'error.type'),
data_get($data, 'error.message'),
];
}
$error = data_get($data, 'error');
if ($error === null) {
return [null, $rawBody ?: null];
}
// Flat string error: {"error": "some string"}
if (is_string($error)) {
return [null, $error];
}
// Standard google.rpc.Status or OpenAI-compatible
$errorType = data_get($error, 'status') ?? data_get($error, 'type');
$errorMessage = data_get($error, 'message');
// Append field violation details
$details = data_get($error, 'details', []);
if (is_array($details)) {
foreach ($details as $detail) {
if (data_get($detail, '@type') === 'type.googleapis.com/google.rpc.BadRequest') {
$violations = data_get($detail, 'fieldViolations', []);
if (is_array($violations) && $violations !== []) {
$parts = [];
foreach ($violations as $v) {
$field = data_get($v, 'field', '');
$desc = data_get($v, 'description', '');
$parts[] = $field !== '' ? "{$field}: {$desc}" : $desc;
}
$errorMessage = ($errorMessage ?? '').'. Field violations: '.implode('; ', $parts);
}
}
}
}
return [$errorType, $errorMessage];
}
The extracted error type and message are used to create a detailed PrismException.
Catching exceptions
Basic error handling
use Prism\Prism\Prism;
use Prism\Prism\Exceptions\PrismException;
use Prism\Vertex\Enums\Vertex;
try {
$response = Prism::text()
->using(Vertex::Gemini, 'gemini-2.5-flash')
->withPrompt('Explain quantum computing')
->asText();
echo $response->text;
} catch (PrismException $e) {
// Handle any Prism error
logger()->error('Vertex AI request failed', [
'message' => $e->getMessage(),
'code' => $e->getCode(),
]);
}
Handling specific error types
use Prism\Prism\Prism;
use Prism\Prism\Exceptions\PrismException;
use Prism\Prism\Exceptions\PrismRateLimitedException;
use Prism\Prism\Exceptions\PrismRequestTooLargeException;
use Prism\Prism\Exceptions\PrismProviderOverloadedException;
use Prism\Vertex\Enums\Vertex;
try {
$response = Prism::text()
->using(Vertex::Gemini, 'gemini-2.5-flash')
->withPrompt('Explain quantum computing')
->asText();
} catch (PrismRateLimitedException $e) {
// Wait and retry
sleep(5);
// Retry logic here
} catch (PrismRequestTooLargeException $e) {
// Request is too large, reduce input size
logger()->warning('Request too large, try reducing prompt length');
} catch (PrismProviderOverloadedException $e) {
// Provider is overloaded, use exponential backoff
logger()->warning('Provider overloaded, retrying later');
} catch (PrismException $e) {
// Other errors
logger()->error('Request failed: ' . $e->getMessage());
}
Retry strategies
You can configure automatic retries using Prism’s built-in retry mechanism:
use Prism\Prism\Prism;
use Prism\Vertex\Enums\Vertex;
$response = Prism::text()
->using(Vertex::Gemini, 'gemini-2.5-flash')
->withClientRetry(
maxAttempts: 3,
delayMs: 1000,
multiplier: 2.0,
retryOnTimeout: true
)
->withPrompt('Explain quantum computing')
->asText();
This will retry the request up to 3 times with exponential backoff (1s, 2s, 4s).
Manual retry with exponential backoff
use Prism\Prism\Prism;
use Prism\Prism\Exceptions\PrismRateLimitedException;
use Prism\Prism\Exceptions\PrismProviderOverloadedException;
use Prism\Vertex\Enums\Vertex;
$maxRetries = 3;
$baseDelay = 1; // seconds
for ($attempt = 0; $attempt < $maxRetries; $attempt++) {
try {
$response = Prism::text()
->using(Vertex::Gemini, 'gemini-2.5-flash')
->withPrompt('Explain quantum computing')
->asText();
break; // Success, exit loop
} catch (PrismRateLimitedException | PrismProviderOverloadedException $e) {
if ($attempt === $maxRetries - 1) {
throw $e; // Last attempt failed
}
$delay = $baseDelay * (2 ** $attempt);
logger()->warning("Attempt {$attempt} failed, retrying in {$delay}s");
sleep($delay);
}
}
Common error scenarios
Invalid model name
try {
Prism::text()
->using(Vertex::Gemini, 'invalid-model-name')
->withPrompt('Hello')
->asText();
} catch (PrismException $e) {
// Error type: NOT_FOUND or INVALID_ARGUMENT
// Message: "Model not found" or similar
}
Missing configuration
// If project_id and location are missing in Standard mode:
try {
Prism::text()
->using(Vertex::Anthropic, 'claude-3-5-sonnet@20241022')
->withPrompt('Hello')
->asText();
} catch (PrismException $e) {
// Message: "Vertex AI Standard mode requires both project_id
// and location to be configured..."
}
Invalid credentials file
// If credentials file doesn't exist:
try {
Prism::text()
->using(Vertex::Gemini, 'gemini-2.5-flash')
->withPrompt('Hello')
->asText();
} catch (PrismException $e) {
// Message: "Vertex AI credentials file not found: /path/to/file.json"
}
Express mode with partner models
// If using Express mode (no project_id/location) with non-Gemini schema:
try {
Prism::text()
->using(Vertex::Anthropic, 'claude-3-5-sonnet@20241022')
->withPrompt('Hello')
->asText();
} catch (PrismException $e) {
// Message: "Vertex AI Express mode only supports Google models.
// The anthropic apiSchema requires Standard mode with project_id and location."
}
Invalid JSON schema
use Prism\Prism\Prism;
use Prism\Prism\Schema\ObjectSchema;
use Prism\Prism\Schema\StringSchema;
use Prism\Vertex\Enums\Vertex;
$schema = new ObjectSchema(
name: 'answer',
properties: [
new StringSchema('', 'Empty name'), // Invalid: empty name
]
);
try {
Prism::structured()
->using(Vertex::Gemini, 'gemini-2.5-flash')
->withSchema($schema)
->withPrompt('Hello')
->asStructured();
} catch (PrismException $e) {
// Error type: INVALID_ARGUMENT
// Message: Field violations: schema.properties[0].name: must not be empty
}
Debugging failed requests
The PrismException includes the original HTTP response for debugging:
use Prism\Prism\Prism;
use Prism\Prism\Exceptions\PrismException;
use Prism\Vertex\Enums\Vertex;
try {
$response = Prism::text()
->using(Vertex::Gemini, 'gemini-2.5-flash')
->withPrompt('Explain quantum computing')
->asText();
} catch (PrismException $e) {
// Log full error details
logger()->error('Vertex AI error', [
'message' => $e->getMessage(),
'code' => $e->getCode(),
'previous' => $e->getPrevious(),
]);
// Access the original HTTP response if available
if ($previous = $e->getPrevious()) {
if ($previous instanceof \Illuminate\Http\Client\RequestException) {
$statusCode = $previous->response->getStatusCode();
$body = $previous->response->body();
$json = $previous->response->json();
logger()->debug('HTTP response', [
'status' => $statusCode,
'body' => $body,
'json' => $json,
]);
}
}
}
All PrismException instances thrown by Vertex include detailed error information extracted from the API response, making it easier to diagnose and fix issues.
Testing error handling
You can test error handling using Laravel’s HTTP fake:
use Illuminate\Support\Facades\Http;
use Prism\Prism\Prism;
use Prism\Prism\Exceptions\PrismRateLimitedException;
use Prism\Vertex\Enums\Vertex;
Http::fake([
'*' => Http::response([
'error' => [
'code' => 429,
'message' => 'Rate limit exceeded',
'status' => 'RESOURCE_EXHAUSTED',
],
], 429),
]);
try {
Prism::text()
->using(Vertex::Gemini, 'gemini-2.5-flash')
->withPrompt('Hello')
->asText();
} catch (PrismRateLimitedException $e) {
// Expected
$this->assertStringContainsString('Rate limit', $e->getMessage());
}