All error responses from Dike follow the RFC 7807 Problem Details specification with Content-Type: application/problem+json; charset=utf-8.
{
"type": "https://dike.it.com/errors/citation_not_found",
"title": "Citation Not Found",
"status": 404,
"detail": "Citation 'القانون رقم 999 لسنة 2099' parsed successfully but does not exist in the indexed corpus.",
"code": "citation_not_found",
"parsed": {
"doc_type": "legislation",
"number": "999",
"year": 2099
}
}Always Match on 'code'
Never branch your code on HTTP status alone or attempt to regex detail/title. The code string is guaranteed stable across all API versions.
Complete Error Code Catalog
| HTTP Status | Machine code | Trigger Condition | Recommended Remediation |
|---|---|---|---|
400 / 422 | validation_error | Request payload missing required fields (query, question, citation) or parameter outside valid bounds (top_k > 50, depth > 3). | Check schema definitions in API Reference. |
401 | unauthorized | Missing Authorization header, invalid token format, or revoked API key. | Confirm key begins with dike_live_ and is present in dashboard. |
403 | plan_required | Attempting to access an endpoint not permitted by current plan (e.g. POST /v1/reason on Free tier). | Upgrade account to Pay As You Go in Developer Dashboard. |
404 | not_found | Target canonical_id does not exist on GET /v1/document/{id} or POST /v1/graph/traverse. | Verify canonical ID format against /v1/search or /v1/cite. |
404 | citation_not_found | Citation parsed cleanly into legal parameters on POST /v1/cite, but no matching statute exists in database. | Check Jurisdictions & Coverage matrix. |
422 | citation_unparseable | String passed to POST /v1/cite did not match any recognized Arabic legal citation grammar. | Inspect detail for attempted normalized form; reformat input text. |
429 | rate_limited | Exceeded per-minute sliding window rate limit. | Read X-RateLimit-Reset and backoff with jitter. |
503 | upstream_unavailable | Downstream search index, document store, or reasoning service is temporarily unreachable. | Retry request with exponential backoff (e.g. 1s, 2s, 4s). |
Production Error Handling Snippets
import time
import httpx
def robust_dike_call(url: str, payload: dict, api_key: str, max_retries: int = 3):
headers = {"Authorization": f"Bearer {api_key}", "Content-Type": "application/json"}
for attempt in range(max_retries):
try:
response = httpx.post(url, headers=headers, json=payload, timeout=15.0)
if response.is_success:
return response.json()
problem = response.json()
error_code = problem.get("code", "unknown_error")
match error_code:
case "rate_limited":
reset_time = int(response.headers.get("X-RateLimit-Reset", time.time() + 10))
sleep_sec = max(1, reset_time - int(time.time()))
print(f"Rate limited. Waiting {sleep_sec}s...")
time.sleep(sleep_sec)
continue
case "upstream_unavailable":
backoff = 2 ** attempt
print(f"Upstream blip (503). Backing off {backoff}s...")
time.sleep(backoff)
continue
case "citation_not_found":
print(f"Citation not in corpus: {problem.get('detail')}")
return None
case "plan_required":
raise PermissionError("Plan upgrade required for /v1/reason.")
case _:
raise RuntimeError(f"Dike API Error [{response.status_code}] {error_code}: {problem.get('detail')}")
except httpx.RequestError as e:
if attempt == max_retries - 1:
raise
time.sleep(2 ** attempt)
raise RuntimeError("Max retries exceeded.")interface ProblemDetails {
type: string;
title: string;
status: number;
detail: string;
code: string;
}
async function callDikeSafe<T>(endpoint: string, body: any): Promise<T> {
const apiKey = process.env.DIKE_API_KEY;
const res = await fetch(`https://api.dike.it.com${endpoint}`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${apiKey}`,
'Content-Type': 'application/json',
},
body: JSON.stringify(body),
});
if (res.ok) {
return (await res.json()) as T;
}
const problem: ProblemDetails = await res.json().catch(() => ({
type: 'unknown',
title: res.statusText,
status: res.status,
detail: 'Non-JSON error response from server',
code: 'unknown_error',
}));
switch (problem.code) {
case 'rate_limited': {
const reset = res.headers.get('X-RateLimit-Reset');
throw new Error(`Rate limited. Window resets at: ${reset}`);
}
case 'plan_required':
throw new Error('This endpoint requires a Pay As You Go subscription.');
case 'citation_not_found':
console.warn('Citation parsed but not indexed in corpus:', problem.detail);
return null as T;
default:
throw new Error(`Dike Error (${problem.code}): ${problem.detail}`);
}
}