الأخطاء
شكل واحد، ومستويان، ومعرّف طلب على كل شيء.
الغلاف
type مجموعة مجمّدة يمكنك التفريع عليها ولن تكبر أبدًا. وcode محدّد وإضافي، فعامِل ما لا تعرفه منه على أنه type الخاص به. وrequestId موجود على كل استجابة، بما فيها الناجحة، وهو ما يربط بلاغًا بسطر في السجل.
{ "error": { "type": "validation_error", "code": "invalid_email_address", "message": "Not a valid email address: ada@", "param": "to.0", "docUrl": "https://openemail.uk/docs/api/errors#invalid_email_address", "requestId": "req_e103790543af4…" }}param منقوط ومفهرس، فيشير إلى العنصر بعينه (to.0 وattachments.2.filename) لا إلى الحقل الذي يحويه.
رموز الحالة
| الحالة | type | رموز شائعة |
|---|---|---|
| 400 | invalid_request_error | malformed_json, invalid_idempotency_key |
| 401 | authentication_error | missing_api_key, invalid_api_key, revoked_api_key, invalid_credential_type |
| 403 | permission_error | insufficient_scope, from_address_forbidden |
| 404 | not_found_error | resource_not_found |
| 409 | conflict_error | email_not_cancellable, translation_not_configured |
| 422 | validation_error | invalid_email_address, reserved_header, too_many_recipients, unknown_parameter, idempotency_key_reuse, label_not_directly_settable, unknown_language, translation_too_long |
| 429 | rate_limit_error | send_quota_exceeded, too_many_inboxes |
| 500 | api_error | internal_error |
| 503 | api_error | translation_failed |
الرمز 404 لا يفرّق أبدًا بين "غير موجود" و"يخص مساحة عمل أخرى". وذلك متعمَّد: فالفرق نفسه معلومة.
الرمز 429 لا يحمل Retry-After أبدًا، فاختر مدة انتظارك بنفسك. وsend_quota_exceeded هو بدل الإرسال الشهري ويُصفَّر أول الشهر، فاعرضه على شخص بدل التراجع التدريجي. وtoo_many_inboxes هو سقف سكّ صناديق الوارد المؤقتة، وتمديد صندوق تملكه بالفعل لا يكلّف شيئًا منه.
الرمزان 500 و503 يتشاركان type ويعنيان لمستدعٍ أمرين مختلفين. فـ 503 اعتمادية لم تُجب (وهي اليوم المترجم)، والطلب يستحق إعادة المحاولة دون تغيير؛ أما 500 فمن عندنا ويستحق الإبلاغ عنه مع requestId الخاص به.
التعامل معها
فرّع على type من أجل السلوك واقرأ code من أجل الرسالة التي تعرضها على إنسان. وcode غير المعروف ليس خطأً في عميلك. إنه يعني أننا سمّينا إخفاقًا بدقة أكبر مما كنا نفعل.
const res = await fetch(`${BASE}/emails`, { method: 'POST', headers, body }); if (!res.ok) { const { error } = await res.json(); switch (error.type) { case 'rate_limit_error': throw new RateLimited(error.code); case 'validation_error': // error.param points at the offending field throw new BadRequest(`${error.param}: ${error.message}`); case 'authentication_error': // revoked_api_key and expired_api_key are worth telling an operator apart throw new AuthFailed(error.code); default: // quote requestId when you report it throw new Unexpected(error.message, error.requestId); }}