Packages and API
Errors
Every error the core packages throw, in Laravel and Node.js, with when it happens and what to do.
How errors work
When Rekhu refuses a call, the core package throws. Every refusal is one base class with a class of its own under it, so you catch exactly what you handle:
- Laravel:
Rekhu\Core\Exceptions\RekhuException, with the classes underRekhu\Core\Exceptions. - Node.js:
RekhuError, with the classes exported from@rekhu/core.
Each carries status (the HTTP status Rekhu gave, or 0 when the call was not made) and type (Rekhu's error type), and a message that says what was wrong.
The error types
| Class (both packages) | Status | Type | When | What to do |
|---|---|---|---|---|
InvalidKey |
401 | no key, a revoked key, or a key made on another Rekhu platform | put the app key from the project's Connection tab in REKHU_APP_KEY |
|
ModuleNotEnabled |
403 | module_not_enabled |
the knower's module is off for this application | call modules() and show only what may be used |
RegionUnavailable |
403 | region_unavailable |
the account is set to Europe only, and this call has no engine in the EU | see Privacy and data region |
CreditExhausted |
402 | the account's credit is used up | top up; see Credit and usage | |
AppPaused |
402 | app_limit |
the application is paused, or at its monthly limit | resume it, or raise the limit, in the account |
FileNotFound |
404 | a kept file is past its day, removed, or of another account | send the file again | |
InvalidRequest |
422, 400 | refused before anything ran: an unknown order key, fix or option, a font not in the library | read the message, which names what was wrong; nothing is charged | |
UnsafeFile |
422 | refused_unsafe |
the file is not safe; safety holds the report |
tell the customer why, ask for another file; see File safety |
RateLimited |
429 | too many calls at once, after the package tried again | wait, and try again later | |
Unavailable |
502, 503 | connection_error when Rekhu could not be reached |
Rekhu, or a service behind it, could not answer after the package tried again | try again in a moment |
LimitReached |
429 | limit_reached |
one of your own limits a day; scope is tenant or user; nothing was sent to Rekhu |
tell the user, or raise the limit in your config |
RekhuException / RekhuError |
any | configuration_error, timeout, upload_failed, … |
any other refusal, such as a knower not in REKHU_KNOWERS, or an upload still being checked |
read the message |
UnsafeFile carries safety, and LimitReached carries scope. Both extend the base class, so a catch of the base class catches them too.
Catch what you handle
use Rekhu\Core\Exceptions\CreditExhausted;
use Rekhu\Core\Exceptions\InvalidRequest;
use Rekhu\Core\Exceptions\LimitReached;
use Rekhu\Core\Exceptions\RekhuException;
use Rekhu\Core\Exceptions\UnsafeFile;
use Rekhu\Core\Rekhu;
try {
$check = Rekhu::seshat()->for($user)->check($request->file('artwork'), $order);
} catch (UnsafeFile $e) {
return back()->withErrors(['artwork' => $e->getMessage()]); // $e->safety holds the report
} catch (LimitReached $e) {
return back()->withErrors(['artwork' => 'Today\'s checks are used up.']); // $e->scope: tenant or user
} catch (InvalidRequest $e) {
report($e); // a mistake in the order or the fixes
throw $e;
} catch (CreditExhausted $e) {
Log::critical('Rekhu credit is used up');
return back()->withErrors(['artwork' => 'Checking is not available right now.']);
} catch (RekhuException $e) {
Log::error('Rekhu refused', ['status' => $e->status, 'type' => $e->type, 'message' => $e->getMessage()]);
throw $e;
}
import { CreditExhausted, InvalidRequest, LimitReached, RekhuError, UnsafeFile } from '@rekhu/core';
try {
const check = await rekhu.seshat({ user }).check(buffer, { name: 'flyer.pdf', order });
res.json(check);
} catch (error) {
if (error instanceof UnsafeFile) return res.status(422).json({ message: error.message, safety: error.safety });
if (error instanceof LimitReached) return res.status(429).json({ message: 'Today\'s checks are used up.', scope: error.scope });
if (error instanceof InvalidRequest) throw error; // a mistake in the order or the fixes
if (error instanceof CreditExhausted) {
logger.error('Rekhu credit is used up');
return res.status(503).json({ message: 'Checking is not available right now.' });
}
if (error instanceof RekhuError) logger.error('Rekhu refused', { status: error.status, type: error.type, message: error.message });
throw error;
}
Catch the specific classes before the base class: the first catch that matches wins.
Retries and double charges
The packages try a call again by themselves, so most short failures never reach your code:
- When Rekhu cannot be reached. A dropped connection, or a 502, 503 or 504 that does not come from Rekhu itself (for example for a moment while Rekhu is being updated), is tried up to four times, waiting 2, 4 and 8 seconds.
- When Rekhu is busy. A 429 or 529, or an answer of type
overloaded_errororrate_limit_error, is tried up to three times, waiting as long as Rekhu'sretry-afterasks, up to 5 seconds. - What is not. Every other failure is thrown at once as the class for it.
- No double charges. Every charged call carries an idempotency key. When a retry reaches Rekhu after the first call already ran, Rekhu answers the first result again, with
meta.replayed, and charges nothing. - Parts of a large file. Each part of an upload in parts is sent again up to three times on its own. When it still fails, resume the upload; see Large files.
What is never charged
A refused call costs nothing: an InvalidRequest, an UnsafeFile, a ModuleNotEnabled, and a LimitReached, which is never sent. A fix with nothing to do is not charged either.
Errors in the assistant
The assistant packages handle Rekhu's refusals themselves. When the project's assistant is paused, at its monthly limit, or out of credit, your users read that the assistant is not available right now, and your app's log says why. In Laravel, php artisan rekhu:assistant checks the key and the credit; in Node.js, assistant.check() does.
The chat routes answer errors as JSON with a message:
| Status | Meaning |
|---|---|
| 401 | the user's sign-in expired, or user() returned null |
| 403 | access or accessUsing refused this user, or (Node.js) a request without the X-Rekhu-Protocol header |
| 419 | the CSRF token expired (Laravel, cookie sign-in) |
| 429 | a cap was reached, such as the public chat's caps per visitor |
The widget shows the message, and asks the user to reload on 401 and 419.
Next
- The core package: install, configuration and the ledger.
- File safety: the safety report behind
UnsafeFile.