Documentation ·وثائق
أدلة المستهلك

الأخطاء ورموز الحالة وإعادة المحاولة

الردود التي تعيدها البوابة نفسها، ومعنى كلٍّ منها وما تفعله، ومتى تستحق إعادة المحاولة.

اقرأ هذه الصفحة بـ English

في هذه الصفحة

نظرة سريعة#

تردّ البوابة حين ترفض طلباً بجسم JSON فيه حقل message. ويحمل كل رد، ردّ البوابة وردّ المزوّد، ترويسة X-Kong-Request-Id. وأي خطأ تصدره خدمة المزوّد نفسها يمرّ دون تغيير، بحالة المزوّد وجسمه.

{
  "message": "No active subscription for this product"
}

الاستدعاء الذي لا تستطيع البوابة مطابقته مع اشتراك يحصل على 403 لا 401، ما لم يكن المضيف يعمل بالصيغة الأحدث الموصوفة أدناه. ويشمل ذلك استدعاءً بلا مفتاح، وبمفتاح مجهول، وبمفتاح البيئة الأخرى، وبمفتاح في ترويسة باسم آخر. لا تفرّق البوابة بين هذه الحالات، فالرسالة واحدة.

ردود البوابة#

الحالةmessageمعناها وما تفعله
403No active subscription for this productلم تستطع البوابة مطابقة مفتاحك مع اشتراك نشط في هذا المنتج. تحقق من المضيف واسم الترويسة والمفتاح وبيئته، ثم من إضافة المنتج إلى التطبيق. انظر الجدول التالي.
403Subscription is not activeتعرف البوابة الاشتراك لكنه غير نشط، كأن يكون قد انتهى، أو يكون تجديده بانتظار الدفع أو الموافقة. افتح اشتراكاتي.
403Subscription quota exceededنفدت حصة اشتراكك. لا تكرّر المحاولة.
429Endpoint quota exceededنفدت حصة نقطة النهاية هذه. لا تكرّر المحاولة.
403No endpoint quota rule found for this consumerالمنتج مسعَّر لكل نقطة نهاية وليست لاشتراكك حصة لهذه النقطة. تحقق من نقاط نهاية اشتراكك.
403Authentication required، Consumer not identifiedلم تستطع البوابة تحديد التطبيق. أرسل المفتاح في ترويسة api360key.
404no Route matched with those valuesلا يوجد توجيه (route) يطابق الطلب. تحقق من عنوان البوابة ومعرّف المنتج و/v والإصدار والمسار والطريقة. ويحمل الجسم أيضاً request_id.
503Subscription service unavailable، Endpoint quota service unavailableجزء من البوابة غير متاح مؤقتاً. أعد المحاولة مع زيادة مدة الانتظار تدريجياً.
500Route configuration errorالمسار مهيّأ خطأً من جهة المنصة. تواصل مع الدعم واذكر معرّف الطلب.
400Endpoint quota rule misconfiguredقاعدة حصة نقطة النهاية خاطئة من جهة المنصة. تواصل مع الدعم واذكر معرّف الطلب.

حين تقرأ 403 مع No active subscription for this product فابحث عن خطئك في هذا الجدول:

ما فعلتهما تغيّره
لم ترسل مفتاحاً، أو وضعته في ترويسة أخرى مثل Authorization أو x-api-keyأرسل api360key: <key>. ويُقبل المفتاح أيضاً معاملَ استعلام باسم api360key، لكن عنوان URL يُكتب في السجلات، ففضّل الترويسة.
استخدمت مفتاح تطبيق «اختبار» على بوابة الإنتاج، أو العكساستخدم مفتاح تطبيق من بيئة البوابة (Sandbox وبيئة الإنتاج).
حذفت المفتاحاستخدم مفتاحاً ما زال موجوداً في التطبيق (التطبيقات والمفاتيح).
لم تضف المنتج إلى التطبيق، أو لم تشتركاشترك ثم أضف المنتج إلى التطبيق (الاشتراكات).
استدعيت بعد ثوانٍ من إجراء التغييرانتظر دقيقة وأرسل الطلب من جديد.
# a key the gateway does not know: the same answer as no key at all
curl -sS "$API360_BASE_URL$API360_PATH" -H "api360key: not-a-real-key"
# a key sent in another header is not read
curl -sS -o /dev/null -w "%{http_code}\n" "$API360_BASE_URL$API360_PATH" -H "x-api-key: not-a-real-key"
# no product behind this path
curl -sS "$API360_GATE_URL/no-such-product/v1/x"

بوابات بصيغة الرد الأحدث#

تردّ بعض عناوين البوابة بصيغة أحدث. الرد بهذه الصيغة فيه كائن JSON اسمه error ويحتفظ برموز الحالة نفسها. اعتمد على code في معالجة الخطأ: أما message فبالعربية أو الإنجليزية بحسب ترويسة Accept-Language في طلبك.

{
  "error": {
    "code": "api_key_missing",
    "message": "No API key was provided. Send your key in the api360key header.",
    "request_id": "03284e42d54f8e25b7cce16b65f7a86a",
    "docs_url": "https://api360.sa/docs/consumer/errors-and-retries#api_key_missing"
  }
}

في مضيف كهذا يكون الاستدعاء بلا مفتاح 401، وللمفتاح المفقود أو غير الصالح رمزه الخاص. والرموز التي تقابلها غالباً فيما يلي. وdocs_url في الرد يشير إلى رمزه أدناه.

api_key_missing#

401. لم يحمل الطلب مفتاحاً. أرسله في ترويسة api360key.

api_key_invalid#

401. لا تعرف البوابة المفتاح أو بيانات الاعتماد، فهي خاطئة أو محذوفة أو من البيئة الأخرى. تحقق من المفتاح مقابل التطبيق والمضيف.

access_denied#

403. المفتاح صالح لكن التطبيق غير مسموح له بهذا المنتج. أضف المنتج إلى التطبيق.

subscription_required#

403. المفتاح صالح ولا يوجد اشتراك نشط في هذا المنتج. اشترك فيه.

subscription_inactive#

403. تعرف البوابة الاشتراك لكنه غير نشط، كأن يكون قد انتهى، أو يكون تجديده بانتظار الدفع أو الموافقة.

subscription_not_started#

403. يبدأ الاشتراك لاحقاً. أعد المحاولة بعد تاريخ بدئه.

subscription_expired#

403. انتهى الاشتراك. جدّده.

quota_exceeded#

429. نفدت حصتك. لا تكرّر المحاولة إلا بعد المدة المذكورة في Retry-After، أو غيّر اشتراكك.

rate_limit_exceeded#

429. طلبات كثيرة جداً. أعد المحاولة بعد المدة المذكورة في Retry-After.

route_not_found#

404. لا يوجد منتج يطابق المضيف والمسار والطريقة.

service_unavailable#

503. الخدمة غير متاحة مؤقتاً. أعد المحاولة مع زيادة مدة الانتظار تدريجياً، بعد المدة المذكورة في Retry-After.

upstream_error#

502. ردّت خدمة المزوّد بجواب غير صالح أو تعذّر الوصول إليها. أعد المحاولة مع زيادة مدة الانتظار تدريجياً.

توجد رموز أخرى للأغراض نفسها، وتقول message فيها ما تفعله. وإذا جاء 500 ومعه معرّف الطلب في الجسم فتواصل مع الدعم.

إعادة المحاولة مع انتظار متزايد وعشوائية في المدة#

أعد المحاولة في الإخفاقات المؤقتة فقط: خطأ الشبكة، والردود 502 و503 و504. أعدها ثلاث مرات على الأكثر، وانتظر مدة عشوائية تطول في كل مرة، حتى لا يعيد كل عملائك المحاولة في اللحظة نفسها. ولا تُعِد أبداً إرسال طلب رُفض بسبب حصة أو اشتراك (403 و429 كما سبق)، ولا 404، ولا أي 4xx آخر: الانتظار لا يغيّر الجواب.

const RETRY = new Set([502, 503, 504]);
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));

// call(fn): fn sends the request and returns a Response; the result is the first answer that is not worth a retry
async function call(fn, { tries = 3, baseMs = 1000 } = {}) {
  for (let i = 0; ; i++) {
    let res;
    try {
      res = await fn();
    } catch (err) {
      if (i >= tries - 1) throw err; // a network error: try again, then give up
      await sleep(2 ** i * baseMs + Math.random() * 0.4 * baseMs);
      continue;
    }
    if (!RETRY.has(res.status) || i >= tries - 1) return res;
    await sleep(2 ** i * baseMs + Math.random() * 0.4 * baseMs);
  }
}

// demo with a fake service that answers 503 twice and then 200
let sent = 0;
const res = await call(async () => new Response("{}", { status: ++sent < 3 ? 503 : 200 }), { baseMs: 1 });
console.log(res.status === 200 ? `ok:${sent}` : `failed:${res.status}`);

الطلب الذي يغيّر بيانات ليس آمناً أن يُعاد بعد انقضاء المهلة: فقد تكون المحاولة الأولى نجحت، وتنفّذ الثانية العمل مرتين. لا تعد مثل هذا الطلب إلا إذا قال المنتج إن ذلك آمن.

حين تحتاج إلى الدعم#

أرسل قيمة X-Kong-Request-Id للرد، ووقت الاستدعاء، والمنتج، والبيئة (احصل على المساعدة). وفي بوابة بالصيغة الأحدث أرسل أيضاً request_id من الجسم.

مخطّط له

الصيغة الأحدث للرد في كل عناوين البوابة، وRetry-After في ردود تحديد المعدل، وترويسة Idempotency-Key التي تجعل إعادة طلب POST آمنة.

انظر أيضاً#

آخر تحديث 2026-10-20