PHPackages                             airouter/openai-compatible-errors - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. airouter/openai-compatible-errors

ActiveLibrary

airouter/openai-compatible-errors
=================================

Safe OpenAI-compatible API error normalization, conservative retry planning, redaction, and SSE replay boundaries for PHP

v0.1.1(today)01↑2900%MITPHPPHP &gt;=8.1CI passing

Since Aug 8Pushed todayCompare

[ Source](https://github.com/airouter-dev/openai-compatible-errors-php)[ Packagist](https://packagist.org/packages/airouter/openai-compatible-errors)[ Docs](https://ai-router.dev/)[ RSS](/packages/airouter-openai-compatible-errors/feed)WikiDiscussions main Synced today

READMEChangelog (2)DependenciesVersions (3)Used By (0)

airouter/openai-compatible-errors
=================================

[](#airouteropenai-compatible-errors)

airouter/openai-compatible-errors is a PHP 8.1+ library for the failure boundary around OpenAI-compatible HTTP APIs. It turns varied gateway, SDK and JSON error shapes into a small immutable object; parses Retry-After; makes replay safety explicit before retrying; redacts bounded diagnostics; and incrementally inspects Chat Completions or Responses Server-Sent Events (SSE).

It has no runtime dependencies. It does not send requests, sleep, retry automatically, buffer an entire stream, or retain raw provider payloads. The application owns transport, cancellation, idempotency, billing and replay.

Project resources
-----------------

[](#project-resources)

The [AI-ROUTER API gateway](https://ai-router.dev/) is the service context for the compatible-endpoint examples. This package remains transport-neutral and can be used with any gateway or provider that follows the same API shape.

The design is grounded in the [OpenAI error-code guide](https://developers.openai.com/api/docs/guides/error-codes), the [MDN Retry-After reference](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Retry-After), and the [WHATWG Server-Sent Events specification](https://html.spec.whatwg.org/multipage/server-sent-events.html). For a worked replay-safety model, read the [LLM stream retry-safety walkthrough](https://ai-router.hashnode.dev/rust-llm-stream-retry-safety). The repository also includes a [PHP failure-boundary article](https://github.com/airouter-dev/openai-compatible-errors-php/blob/main/marketing/php-openai-compatible-api-failure-boundary.md)with the decision rationale and bounded examples.

Install this implementation from [Packagist](https://packagist.org/packages/airouter/openai-compatible-errors). Teams using Ruby can compare the [native RubyGems implementation](https://rubygems.org/gems/openai-compatible-errors); the packages share a safety boundary but do not share runtime code.

Install
-------

[](#install)

```
composer require airouter/openai-compatible-errors

```

The package uses PSR-4 autoloading under AiRouter\\OpenAICompatibleErrors and works beside Guzzle, Symfony HttpClient, Laravel HTTP, PSR-18 clients or a native cURL integration without selecting one of them as a dependency.

Normalize a failure safely
--------------------------

[](#normalize-a-failure-safely)

Pass a response-like array or an SDK exception. Provider-controlled message text is omitted by default:

```
use AiRouter\OpenAICompatibleErrors\OpenAICompatibleErrors;

$error = OpenAICompatibleErrors::normalizeError(
    status: 429,
    headers: [
        'retry-after' => '2',
        'x-request-id' => 'req_php_01',
    ],
    body: [
        'error' => [
            'type' => 'requests',
            'code' => 'rate_limit_exceeded',
            'message' => 'provider detail',
        ],
    ],
);

$error->category->value;       // rate_limit
$error->status;                // 429
$error->retryAfterMs;          // 2000
$error->requestId;             // req_php_01
$error->providerMessage;       // null

$logger->warning('AI API failure', $error->toLogArray());

```

ApiError exposes stable library-owned message text and validated short identifiers. It has no raw body, headers, exception cause, traceback, prompt or generated-output field. If an operator genuinely needs provider text, opt in explicitly; common bearer and API-key formats are still redacted and bounded:

```
$diagnostic = OpenAICompatibleErrors::normalizeError(
    $exception,
    includeProviderMessage: true,
);
$logger->warning(
    'AI API failure',
    $diagnostic->toLogArray(includeProviderMessage: true),
);

```

The opt-in reduces risk; it is not permission to log arbitrary customer data.

### PSR-style response metadata without a transport dependency

[](#psr-style-response-metadata-without-a-transport-dependency)

The normalizer reads public status/statusCode and headers properties, and recognizes getStatusCode and getHeaders when an object exposes those standard methods. It deliberately does not consume a response stream through getBody. Pass the body explicitly at the boundary:

```
$error = OpenAICompatibleErrors::normalizeError(
    $exception,
    status: $response->getStatusCode(),
    headers: $response->getHeaders(),
    body: (string) $response->getBody(),
);

```

Classification prefers HTTP status, structured error.code/error.type and class names. Free-form exception messages are not retained and are only a last-resort signal for transport categories.

Categories include authentication, permission, rate\_limit, quota, conflict, validation, not\_found, payload\_too\_large, timeout, network, upstream, server, schema, endpoint, aborted, stream and unknown. A 409 conflict remains a manual decision because the library cannot infer how the application should resolve state.

Plan retries, never replay blindly
----------------------------------

[](#plan-retries-never-replay-blindly)

An HTTP method does not prove that a request is safe to replay. Supply the operation contract and phase in which the failure happened:

```
use AiRouter\OpenAICompatibleErrors\Retry\ReplaySafety;
use AiRouter\OpenAICompatibleErrors\Retry\RequestPhase;
use AiRouter\OpenAICompatibleErrors\Retry\RetryContext;

$context = new RetryContext(
    method: 'POST',
    phase: RequestPhase::HttpError,
    replaySafety: ReplaySafety::Safe,
    attempt: 1,
    elapsedMs: 350,
);

$plan = OpenAICompatibleErrors::decideRetry($error, $context);

if ($plan->retry()) {
    $scheduler->after($plan->delayMs ?? 0, fn () => replay_request());
}

```

The result is deliberately three-state:

- retry only for a transient category, known replay-safe operation, known phase, no observed stream output and remaining budgets;
- do\_not\_retry for permanent failures, unsafe replay, cancellation, completion, partial output or exhausted budgets;
- manual\_decision when evidence is missing, unclassified or invalid.

Server Retry-After and millisecond hints are parsed without network calls. Duplicate hints use the longest valid delay. A malformed present hint becomes a bounded sentinel, so the default policy fails closed instead of replacing a server instruction with a short local retry. Local exponential backoff supports full jitter and an injectable random callable for deterministic tests.

The library never sleeps, opens a socket, calls a provider or replays a request.

Inspect streaming replay boundaries
-----------------------------------

[](#inspect-streaming-replay-boundaries)

SseInspector consumes byte chunks incrementally. It handles CRLF/LF framing, UTF-8 split across network chunks, Chat Completions deltas, Responses event names, \[DONE\], provider error events and unexpected EOF. It records state only:

```
use AiRouter\OpenAICompatibleErrors\Sse\SseInspector;

$inspector = new SseInspector();
foreach ($responseBodyChunks as $chunk) {
    $inspector->feed($chunk);
    render_chunk($chunk);
}
$state = $inspector->close();

if ($state->unexpectedEof() && $state->hasOutput) {
    throw new RuntimeException('refuse automatic replay after partial output');
}

```

Terminal states are done, incomplete, error and unexpected\_eof. hasOutput is conservative: a false positive prevents an unsafe replay, while a false negative could duplicate visible output or billing. The inspector never stores generated text or a complete response body.

Bounded log sanitization
------------------------

[](#bounded-log-sanitization)

Use sanitizeForLog for small diagnostic context, not as a data-retention policy:

```
$safe = OpenAICompatibleErrors::sanitizeForLog([
    'provider' => 'example',
    'api_key' => getenv('API_KEY'),
    'attempt' => 2,
]);

```

It redacts sensitive key names and common credential formats, limits depth, nodes, keys, items and characters, and avoids traversing exception messages or arbitrary object properties. Keep real credentials, prompts, completions and customer payloads out of fixtures and logs.

Compatibility and boundaries
----------------------------

[](#compatibility-and-boundaries)

This package targets common OpenAI-compatible shapes used by gateways, self-hosted routers and SDK adapters. It is not an OpenAI product, provider certification or promise of complete parity with a vendor's proprietary event schema. Unknown data-bearing SSE events are treated conservatively because replaying after an unrecognized event can duplicate visible output.

Use a full resilience library when you need circuit breaking, cancellation-aware sleep, hedging or request execution. Keep a provider SDK's native exception when one stable provider contract is all your application needs. The value here is an explicit, auditable boundary across multiple OpenAI-compatible endpoints.

Related language packages
-------------------------

[](#related-language-packages)

- [JavaScript and TypeScript package on npm](https://www.npmjs.com/package/@ai-router/openai-compatible-errors)
- [Python package on PyPI](https://pypi.org/project/openai-compatible-errors/)
- [.NET package on NuGet](https://www.nuget.org/packages/AiRouter.OpenAICompatibleErrors/)
- [JVM contract package on Maven Central](https://central.sonatype.com/artifact/dev.ai-router/openai-compatible-contract-junit)
- [Rust stream guard on crates.io](https://crates.io/crates/llm-stream-guard)

Development
-----------

[](#development)

```
composer validate --strict
composer run verify
composer dump-autoload --classmap-authoritative

```

See CONTRIBUTING.md, SECURITY.md and RELEASING.md for validation, data-boundary and Packagist submission details.

MIT licensed.

###  Health Score

36

—

LowBetter than 79% of packages

Maintenance100

Actively maintained with recent releases

Popularity2

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity33

Early-stage or recently created project

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~0 days

Total

2

Last Release

0d ago

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/299943165?v=4)[AI ROUTER](/maintainers/airouter-dev)[@airouter-dev](https://github.com/airouter-dev)

---

Top Contributors

[![github-actions[bot]](https://avatars.githubusercontent.com/in/15368?v=4)](https://github.com/github-actions[bot] "github-actions[bot] (3 commits)")

---

Tags

streamingopenaisseretry-afterredactionllmopenai-compatibleapi-errorssafe-logging

### Embed Badge

![Health badge](/badges/airouter-openai-compatible-errors/health.svg)

```
[![Health](https://phpackages.com/badges/airouter-openai-compatible-errors/health.svg)](https://phpackages.com/packages/airouter-openai-compatible-errors)
```

###  Alternatives

[mozex/anthropic-php

PHP client for the Anthropic API: messages, streaming, tool use, thinking, web search, code execution, batches, and more.

48614.7k19](/packages/mozex-anthropic-php)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
