معالجة الأخطاء
تنسيق استجابة الخطأ
تتبع جميع أخطاء API بنية JSON متسقة مع رمز حالة HTTP مناسب:
{
"error": {
"code": "ERROR_CODE",
"message": "Human-readable description of what went wrong"
}
}حقل code هو معرّف ثابت قابل للقراءة آلياً يمكنك استخدامه في منطق معالجة الأخطاء. حقل message يوفر سياقاً إضافياً وقد يختلف بين حالات نفس رمز الخطأ.
فئات الأخطاء
أخطاء المصادقة (401)
تُرجع عندما لا يمكن مصادقة الطلب.
| الرمز | الوصف |
|---|---|
UNAUTHORIZED | ترويسة Authorization مفقودة أو مشوّهة أو تحتوي على رمز غير صالح. |
KEY_EXPIRED | كان مفتاح API صالحاً لكنه تجاوز تاريخ انتهاء صلاحيته. أنشئ مفتاحاً جديداً للمتابعة. |
{
"error": {
"code": "UNAUTHORIZED",
"message": "Missing or invalid Authorization header"
}
}أخطاء التفويض (403)
تُرجع عندما لا يملك المستخدم المصادق عليه صلاحية الوصول إلى المورد المطلوب.
| الرمز | الوصف |
|---|---|
FORBIDDEN | تحاول الوصول إلى أو تعديل مورد ينتمي إلى مستخدم آخر (مثال: إلغاء مفتاح API لشخص آخر). |
أخطاء عدم العثور (404)
تُرجع عندما لا يوجد المورد المطلوب.
| الرمز | الوصف |
|---|---|
NOT_FOUND | المورد المحدد في عنوان URL (استخراج أو مفتاح API، إلخ) غير موجود أو تم حذفه. |
أخطاء التحقق (400)
تُرجع عندما يكون جسم الطلب أو المعاملات غير صالحة.
| الرمز | الوصف |
|---|---|
VALIDATION_ERROR | فشل جسم الطلب في التحقق من المخطط. حقل message يصف أي الحقول غير صالحة. |
MAX_KEYS_REACHED | لقد وصلت إلى الحد الأقصى وهو 10 مفاتيح API نشطة. ألغِ مفتاحاً موجوداً قبل إنشاء مفتاح جديد. |
{
"error": {
"code": "MAX_KEYS_REACHED",
"message": "Maximum of 10 active API keys allowed"
}
}أخطاء تحديد المعدل (429)
تُرجع عند تجاوز حدود الاستخدام.
| الرمز | الوصف |
|---|---|
USAGE_LIMIT_EXCEEDED | تم الوصول إلى حد الاستخراج الشهري. قم بترقية خطتك أو انتظر حتى فترة الفوترة التالية. |
{
"error": {
"code": "USAGE_LIMIT_EXCEEDED",
"message": "Monthly extraction limit reached (25/25). Upgrade your plan for more extractions."
}
}أخطاء الخادم (500)
تُرجع عند حدوث شيء غير متوقع على جانب الخادم.
| الرمز | الوصف |
|---|---|
INTERNAL_ERROR | حدث خطأ غير متوقع في الخادم. يتم تسجيل هذه الأخطاء والتحقيق فيها تلقائياً. |
EXTRACTION_FAILED | واجه خط أنابيب معالجة الذكاء الاصطناعي خطأً أثناء استخراج البيانات من مستندك. قد يكون المستند تالفاً أو غير قابل للقراءة أو بتنسيق غير مدعوم. |
الأخطاء القابلة لإعادة المحاولة
ليست جميع الأخطاء يجب إعادة محاولتها. إليك التفصيل:
| الرمز | قابل لإعادة المحاولة | الإجراء |
|---|---|---|
INTERNAL_ERROR | نعم | أعد المحاولة مع تراجع أسي |
EXTRACTION_FAILED | نعم | أعد المحاولة -- قد ينتج الذكاء الاصطناعي نتيجة مختلفة. إذا استمر، تحقق من جودة المستند |
USAGE_LIMIT_EXCEEDED | لا | قم بترقية خطتك أو انتظر حتى فترة الفوترة التالية |
UNAUTHORIZED | لا | أصلح بيانات اعتماد المصادقة |
KEY_EXPIRED | لا | أنشئ مفتاح API جديداً |
FORBIDDEN | لا | تحاول الوصول إلى مورد لا تملكه |
NOT_FOUND | لا | المورد غير موجود -- تحقق من المعرّف |
VALIDATION_ERROR | لا | أصلح جسم الطلب ليطابق المخطط المتوقع |
MAX_KEYS_REACHED | لا | ألغِ مفتاحاً موجوداً قبل إنشاء مفتاح جديد |
استراتيجية إعادة المحاولة
للأخطاء القابلة لإعادة المحاولة، استخدم التراجع الأسي لتجنب إرهاق الخادم. إليك تنفيذاً قابلاً لإعادة الاستخدام:
interface ApiResponse<T> {
data?: T;
error?: { code: string; message: string };
}
async function fetchWithRetry<T>(
url: string,
options: RequestInit,
maxRetries = 3,
): Promise<ApiResponse<T>> {
let lastError: Error | null = null;
for (let attempt = 0; attempt <= maxRetries; attempt++) {
try {
const response = await fetch(url, options);
const body = await response.json();
if (response.ok) {
return body as ApiResponse<T>;
}
const errorCode = body?.error?.code;
// Only retry server errors
if (
(errorCode === "INTERNAL_ERROR" || errorCode === "EXTRACTION_FAILED") &&
attempt < maxRetries
) {
const delayMs = 1000 * Math.pow(2, attempt); // 1s, 2s, 4s
console.log(`Retrying in ${delayMs}ms (attempt ${attempt + 1}/${maxRetries})...`);
await new Promise((resolve) => setTimeout(resolve, delayMs));
continue;
}
// Non-retryable error -- return immediately
return body as ApiResponse<T>;
} catch (err) {
lastError = err as Error;
if (attempt < maxRetries) {
const delayMs = 1000 * Math.pow(2, attempt);
await new Promise((resolve) => setTimeout(resolve, delayMs));
continue;
}
}
}
throw lastError ?? new Error("Max retries exceeded");
}الاستخدام:
const result = await fetchWithRetry<ExtractionResult>(
"https://api.docmap.io/v1/extractions/run",
{
method: "POST",
headers: {
Authorization: `Bearer ${apiKey}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
templateId: "tmpl_abc123",
fileName: "invoice.pdf",
pdfBase64: encodedPdf,
mimeType: "application/pdf",
}),
},
);
if (result.error) {
console.error(`Extraction failed: [${result.error.code}] ${result.error.message}`);
} else {
console.log("Extracted data:", result.data);
}أفضل الممارسات
تحقق دائماً من رمز حالة HTTP أولاً. حالة
2xxتعني النجاح؛ أي شيء آخر هو خطأ. لا تفترض أن جسم الاستجابة يحتوي على حقلdataدون التحقق.حلّل جسم الخطأ للحصول على حقل
code. استخدمcode(وليسmessage) في تدفق التحكم الخاص بك. قد تتغير رسائل الخطأ بمرور الوقت، لكن رموز الأخطاء ثابتة.عالج رموز خطأ محددة. فرّع على الرموز المعروفة مثل
USAGE_LIMIT_EXCEEDEDأوKEY_EXPIREDلعرض رسائل مستهدفة لمستخدميك أو تشغيل سير عمل محدد.سجّل الأخطاء غير المعروفة. إذا واجهت رمز خطأ لا تعالجه بشكل صريح، سجّل الاستجابة الكاملة (رمز الحالة ورمز الخطأ والرسالة) لتصحيح الأخطاء.
لا تعد محاولة أخطاء 4xx. أخطاء المصادقة والتفويض والتحقق وعدم العثور لن تُحل من تلقاء نفسها. أصلح المشكلة الأساسية قبل إعادة المحاولة.
حدد حداً لإعادة المحاولة. لا تعد المحاولة إلى ما لا نهاية. ثلاث محاولات مع تراجع أسي (1 ثانية، 2 ثانية، 4 ثوانٍ) هي الافتراضي المعقول لأخطاء الخادم.
