FailureClassifier

Reading why the platform refused a call — the seven kinds the core acts on, what each makes the core do, and how to map a platform's error vocabulary onto them.

Subscriby\Connector\Contracts\Ports\FailureClassifier. Required of every connector.

Each platform has its own error vocabulary and its own habit of refusing inside a successful HTTP response. The connector reads both shapes; the core decides once, from the kind, whether to retry, wait, alert the creator or give up. Every send, grant, revoke and probe your other ports perform ends in a DeliveryFailure built here when it did not succeed.

The method

public function classify(mixed $responseOrThrowable): DeliveryFailure;

The argument is whatever your platform client gave you: a decoded response body, a response object, or the exception the call raised. Return a DeliveryFailure:

FieldMeaning
kindOne of the seven DeliveryFailureKinds below.
detailThe platform's own words, for logs and the connector doctor. Never a credential.
retryAfterSecondsFor RateLimited: how long the platform asked you to wait.
codeThe platform's error code, when it has one, for your own tests and dashboards.

The seven kinds

KindMeansRetryableCreator-actionable
UnreachableThe account exists but cannot be written to: it blocked the bot, closed its messages, left the platform.
NotPermittedThe installation lacks a right it needs in a place: not an administrator, a missing permission.
TargetMissingThe place, message or account no longer exists.
RateLimitedThe platform asked for a pause; carry its wait in retryAfterSeconds.
ConfigurationThe installation itself is wrong: a revoked token, a deleted bot, a call the manifest says the platform cannot make.
TransientThe platform or the network hiccuped.
OtherNothing above fits. Make it rare.

The two columns are the enum's own methods, DeliveryFailureKind::isRetryable() and isCreatorActionable(), and they are what the core reads: a retryable failure is tried again (after retryAfterSeconds for a rate limit), a creator-actionable one becomes a health verdict the creator is shown with the connector's own remedy (RecoverySupport::healthReasonText()), and everything else is recorded as failed for that call. On a recovery probe of an account, TargetMissing has one more meaning: the account is gone, which is the incident the Disaster Recovery Program exists for, while any other kind is left alone.

Mapping a platform

Write the mapping as a table in your connector, from the platform's codes and phrases to the kinds, and test it row by row. Three habits keep it honest:

  • Read the body, not only the status. Telegram answers 200 OK with "ok": false and a description; Discord answers 403 for four different situations only the JSON code tells apart.
  • Never let a network exception fall through as Other. A timeout, a connection refused, a DNS failure is Transient.
  • Classify what your own client throws. A validation exception from the client library about a message the platform would reject is Configuration, because retrying cannot help.

How Telegram does it

The sentence reading lives in the connector's TelegramRefusal, which the pre-SDK code already had: bot was blocked by the user and user is deactivated are Unreachable, chat not found and message to delete not found are TargetMissing, not enough rights is NotPermitted, Too Many Requests is RateLimited with Telegram's retry_after, Unauthorized (a revoked token) is Configuration. The port adds the two cases the refusal reader had no word for: a bot that lacks rights in a chat, and a network that never answered.

What the kit checks

failures.classifies_anything: classify() returns a DeliveryFailure for an arbitrary throwable and for an arbitrary string, and does not throw itself. A classifier that only understands its own client's exceptions fails here.

One classifier, every port

Your Messenger, AccessController, SupportRelay and RecoverySupport should all build their failures through this class, so the core sees one vocabulary and your tests cover one mapping. The core also calls it directly when a port throws instead of returning.

How is this guide?

On this page

Subscriby is a product designed by you — for you.

No boardroom full of executives deciding what we ships next. Our roadmap always shaped by you with your feedback.

Share feedback or a request