16 β Xatolar, retry va ishonchlilik¶
β¬ οΈ Oldingi: 15 β Prompt caching Β· π README Β· Keyingi: 17 β Embeddings va semantik qidiruv β‘οΈ
Bu bobda: nega ishlab chiqarishda (production) API chaqiruvlari muqarrar ravishda ba'zan barbod bo'lishini va ishonchli klient ularni qanday chiroyli boshqarishini o'rganamiz. Tipli xatolarni β
Anthropic.RateLimitError,Anthropic.AuthenticationError,Anthropic.BadRequestErrorva asosiyAnthropic.APIError(.statusbilan) βinstanceoforqali tutamiz (matn solishtirish bilan emas); to'liq xato jadvalini (400/401/403/404/429/5xx/529) va qaysisini qayta urinish (retry) mumkinligini ko'ramiz. SDK 429/5xx ni o'zi eksponensial backoff bilan qayta urinishini (maxRetries) va qachon o'z retry'ingizni yozish kerakligini bilib olamiz. So'ng qo'lda eksponensial backoff (withRetry) βretry-afterga rioya qilib, urinishlar sonini cheklab β yozamiz. Timeout, oqim (stream) ichidagi xatolar, javob darajasidagi "xatolar" (stop_reason: "refusal"va"max_tokens") va ishonchlilik naqshlari (graceful degradation, fallback,request_idni loglash) ni ko'rib chiqamiz. Yakunda β haqiqiy ilova ishlatishi mumkin bo'lgan ishlab-chiqarishga-tayyorcallClaudeo'rovini quramiz.Halollik eslatmasi: Bobdagi barcha SDK chaqiruvlari
@anthropic-ai/sdkv0.104 ga tayanadi: tipli xato klasslari (Anthropic.BadRequestError,Anthropic.AuthenticationError,Anthropic.PermissionDeniedError,Anthropic.NotFoundError,Anthropic.RateLimitError,Anthropic.APIError,Anthropic.APIConnectionError,Anthropic.InternalServerErrorβ hammasiAPIErrordan meros oladi va.statusga ega),new Anthropic({ maxRetries, timeout }),client.withOptions({ maxRetries }), vamsg._request_id. Bu klass va parametr nomlari haqiqatan mavjud β o'ylab topilgani yo'q.
1. Nega bu bob muhim? β production'da chaqiruvlar barbod bo'ladi¶
Hozirgacha kitobdagi misollarda biz baxtli yo'ldan (happy path) yurdik: await client.messages.create(...) β javob keldi, ishladi. Lokal mashinangizda, bir nechta sinov chaqiruvida bu deyarli har doim shunday. Lekin ilovangizni real foydalanuvchilarga ochsangiz, vaziyat tubdan o'zgaradi.
Minglab so'rov ostida, real tarmoq orqali, real serverga β chaqiruvlar muqarrar ba'zan barbod bo'ladi:
- Rate limit (429). Bir daqiqada ruxsat etilganidan ko'p so'rov yubordingiz. (Limitlar haqida β 14-bob.)
- Tarmoq uzilishi. Foydalanuvchining Wi-Fi'i, serveringizning aloqasi, oraliq proksi β bir lahzaga "yiqildi".
- Server ortiqcha yuklangan (529). Anthropic infratuzilmasida vaqtinchalik yuqori talab.
- Server xatosi (5xx). Bir lahzalik ichki nosozlik.
Analogiya. Tasavvur qiling, har kuni ishga avtobusda borasiz. Ko'p kunlar avtobus o'z vaqtida keladi. Lekin ba'zida β yo'l tirbandligi, sinish, gavjumlik. Yomon yo'lovchi birinchi muammoda taslim bo'ladi: "avtobus kelmadi, demak men bugun ishga bormayman". Yaxshi yo'lovchi esa rejaga ega: bir-ikki daqiqa kutadi (vaqtinchalik tirbandlik o'tib ketadi), keyingi avtobusni kutadi, kerak bo'lsa taksiga (zaxira reja) o'tadi. Ishonchli klient β aynan yaxshi yo'lovchi: vaqtinchalik muammoda taslim bo'lmaydi, balki aqlli ravishda qayta uradi yoki zaxira rejaga o'tadi.
Ishonchsiz klient bu xatolarda qulab tushadi (throw butun ilovani to'xtatadi) yoki foydalanuvchining so'rovini yo'qotadi. Ishonchli klient esa: tushunadi (qaysi xato?), qaror qiladi (qayta urinish mumkinmi?), va chiroyli boshqaradi (qayta urin, yoki zaxira rejaga o't, yoki foydalanuvchiga tushunarli xabar ber). Bu bobning maqsadi β sizning klientingizni yaxshi yo'lovchiga aylantirish.
2. Tipli xatolar β to'g'ri usul (instanceof, matn emas)¶
Xatoni tutganda birinchi savol: bu qanday xato? SDK bunga aniq javob beradi β har HTTP xato kodi uchun alohida klass bor. Ularni instanceof bilan tekshiring.
import Anthropic from "@anthropic-ai/sdk";
const client = new Anthropic(); // ANTHROPIC_API_KEY muhitdan
try {
const msg = await client.messages.create({
model: "claude-opus-4-8",
max_tokens: 1024,
messages: [{ role: "user", content: "Salom!" }],
});
console.log(msg.content);
} catch (err) {
if (err instanceof Anthropic.RateLimitError) {
console.warn("Rate limit (429) β biroz kuting va qayta urining.");
} else if (err instanceof Anthropic.AuthenticationError) {
console.error("Kalit noto'g'ri (401) β ANTHROPIC_API_KEY ni tekshiring.");
} else if (err instanceof Anthropic.BadRequestError) {
console.error("Noto'g'ri so'rov (400):", err.message);
} else if (err instanceof Anthropic.APIError) {
// Asosiy (bazaviy) klass β boshqa hamma API xatolari shu yerga tushadi
console.error(`API xatosi (${err.status}):`, err.message);
} else {
throw err; // API bilan bog'liq bo'lmagan xato β yuqoriga uzating
}
}
E'tibor bering:
err instanceof Anthropic.RateLimitErrorβ bu xato aynan rate limit (429) ekanini ishonchli bildiradi. Klass nomi orqali, taxmin qilmasdan.Anthropic.APIErrorβ barcha API xatolarining bazaviy klassi. Har bir maxsus klass (RateLimitError, BadRequestError, ...) undan meros oladi. Shuning uchun uni oxirgi (eng umumiy) tekshiruv sifatida qo'yamiz β bu yerga yuqorida tutilmagan hamma API xatolari tushadi. Uning.statusmaydoni β HTTP kodi (429,400, ...).
β οΈ Eng muhim qoida: matn (string) solishtirmang. Quyidagi kabi kod β xato amaliyot:
} catch (err) { if (err.message.includes("429") || err.message.includes("rate_limit")) { ... } // β NOTO'G'RI }Xato xabarining matni β bu API ning ichki tafsiloti; u istalgan vaqtda o'zgarishi mumkin, va tilga/formatga bog'liq. Bir kun "rate_limit" deb yozilgan bo'lsa, ertaga boshqacha bo'lishi mumkin β sizning
includes(...)tekshiruvingiz jimgina ishlamay qoladi. Klass tipli β u o'zgarmaydi va xato kodiga qat'iy bog'langan. Har doiminstanceofishlating.
To'liq xato jadvali β qaysini qayta urinish mumkin?¶
| HTTP | Tipli klass | Qayta urinish? | Sabab |
|---|---|---|---|
| 400 | Anthropic.BadRequestError |
β Yo'q | So'rov formati/parametri noto'g'ri |
| 401 | Anthropic.AuthenticationError |
β Yo'q | API kalit noto'g'ri yoki yo'q |
| 403 | Anthropic.PermissionDeniedError |
β Yo'q | Kalitda ruxsat yo'q |
| 404 | Anthropic.NotFoundError |
β Yo'q | Model/endpoint nomi noto'g'ri |
| 429 | Anthropic.RateLimitError |
β Ha | Juda ko'p so'rov (retry-after bor) |
| 500 | Anthropic.InternalServerError |
β Ha | Anthropic server xatosi |
| 529 | Anthropic.OverloadedError |
β Ha | Server vaqtinchalik band |
| β | Anthropic.APIConnectionError |
β Ha | Tarmoq uzildi / ulanib bo'lmadi |
Asosiy mantiq juda sodda:
- 4xx (400/401/403/404) β qayta URINMANG. Bular sizning aybingiz: so'rov, kalit yoki ruxsat noto'g'ri. Aynan o'sha so'rovni qayta yuborsangiz, aynan o'sha xato qaytadi β qayta urinish faqat vaqt va token isrof qiladi. Ularni tuzatish kerak (kodda yoki sozlamada).
- 429, 5xx, ulanish xatolari β qayta URINING. Bular vaqtinchalik: biroz kutib qayta urinsangiz, ko'pincha o'tib ketadi. Aynan shu yerda backoff ishga tushadi.
Bitta istisnoni eslang: 429 β bu 4xx oilasidan, lekin u qayta urinish mumkin bo'lgan yagona 4xx. Chunki u "siz noto'g'ri qildingiz" emas, "hozir juda tez yubordingiz, biroz sekinroq" degani.
3. SDK buni siz uchun qiladi β maxRetries¶
Yaxshi xabar: 429 va 5xx (hamda ulanish) xatolari uchun SDK avtomatik ravishda eksponensial backoff bilan qayta urinadi. Standart sozlama β maxRetries: 2 (ya'ni asl chaqiruv + 2 qayta urinish). Demak ko'p hollarda siz hech qanday retry kodi yozishingiz shart emas β SDK uni o'zi boshqaradi.
Bu sonni o'zgartirish oson β klient yaratishda:
Yoki bitta chaqiruv uchun (klientni o'zgartirmasdan), withOptions orqali:
const msg = await client
.withOptions({ maxRetries: 5 })
.messages.create({
model: "claude-opus-4-8",
max_tokens: 1024,
messages: [{ role: "user", content: "Salom!" }],
});
maxRetries: 0 β retry'ni butunlay o'chiradi.
Demak nega qo'lda retry yozaman? SDK ning avtomatik retry'i ko'p ilovaga yetadi. Lekin quyidagi holatlarda o'z retry'ingiz kerak bo'ladi:
- Uzunroq yoki boshqacha backoff β SDK ning standartidan farqli kutish strategiyasi (masalan, juda uzun kechikishlar, yoki maxsus jitter).
- Maxsus loglash β har bir urinishni o'z log tizimingizga yozish, metrikalar yig'ish.
- Navbat (queueing) β qayta urinish o'rniga so'rovni navbatga qo'yib, keyinroq bajarish.
- Fallback bilan birlashtirish β qayta urinishlar tugagach, boshqa modelga yoki keshlangan javobga o'tish (pastda quramiz).
Boshqacha aytganda: oddiy holatda maxRetries ni sozlang va tinch bo'ling. Murakkabroq mantiq kerak bo'lsa β keyingi bo'limdagi withRetry ni yozing.
4. Qo'lda eksponensial backoff β withRetry¶
Endi o'z retry mantig'imizni yozaylik. Asosiy g'oya β eksponensial backoff: har muvaffaqiyatsiz urinishdan keyin kutish vaqtini ikki baravar oshiramiz. Birinchi muvaffaqiyatsizlikda 1s, keyin 2s, keyin 4s, keyin 8s... Bunga jitter (kichik tasodifiy qo'shimcha) qo'shamiz β bu ko'p klient bir vaqtda qayta urinib serverga "to'p bo'lib bostirib kelishi" (thundering herd) muammosini oldini oladi.
Mana toza implementatsiya:
import Anthropic from "@anthropic-ai/sdk";
// Berilgan millisekundga "uxlaydi" (kutadi)
const uxla = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
// Bu xato qayta urinishga arziydimi?
function retryGaArziydimi(err) {
if (err instanceof Anthropic.RateLimitError) return true; // 429
if (err instanceof Anthropic.APIConnectionError) return true; // tarmoq
if (err instanceof Anthropic.APIError) return err.status >= 500; // 5xx / 529
return false; // 4xx (429'dan tashqari) va boshqalar β qayta urinmaymiz
}
async function withRetry(fn, { maxRetries = 5, base = 1000, cap = 30000 } = {}) {
let oxirgiXato;
for (let urinish = 0; urinish <= maxRetries; urinish++) {
try {
return await fn(); // muvaffaqiyat β natijani darhol qaytaramiz
} catch (err) {
oxirgiXato = err;
// Qayta urinishga arzimasa (4xx) yoki urinishlar tugagan bo'lsa β to'xta
if (!retryGaArziydimi(err) || urinish === maxRetries) {
throw err;
}
// Kechikishni hisoblaymiz: base * 2^urinish + jitter
let kechikish = Math.min(base * 2 ** urinish, cap);
kechikish += Math.random() * 1000; // jitter (0β1s)
// 429 bo'lsa, server aytgan `retry-after` ga rioya qilamiz
if (err instanceof Anthropic.RateLimitError) {
const retryAfter = Number(err.headers?.["retry-after"]);
if (!Number.isNaN(retryAfter) && retryAfter > 0) {
kechikish = Math.max(kechikish, retryAfter * 1000);
}
}
console.warn(
`Urinish ${urinish + 1}/${maxRetries} muvaffaqiyatsiz. ` +
`${Math.round(kechikish)}ms dan keyin qayta...`
);
await uxla(kechikish);
}
}
throw oxirgiXato; // mantiqan bu yerga yetib kelmaydi
}
Foydalanish β istalgan chaqiruvni withRetry ga o'rab:
const client = new Anthropic({ maxRetries: 0 }); // SDK retry'ini o'chiramiz, o'zimizniki ishlaydi
const msg = await withRetry(() =>
client.messages.create({
model: "claude-opus-4-8",
max_tokens: 1024,
messages: [{ role: "user", content: "Salom!" }],
})
);
Bu kodning muhim nuqtalari:
retryGaArziydimi(err)β markaziy qaror. U faqat 429, 5xx va ulanish xatolarini "ha" deb baholaydi; 4xx larni "yo'q". Shuning uchun noto'g'ri so'rov (400) bo'lsa, darholthrowqilinadi β vaqt isrof bo'lmaydi.base * 2 ** urinishβ eksponensial o'sish:urinish=0β 1s,1β 2s,2β 4s,3β 8s.Math.min(..., cap)β kechikish cheksiz o'smasligi uchun yuqori chegara (30s).retry-afterga rioya β 429 da Anthropicretry-aftersarlavhasini qaytaradi (necha soniya kutish kerakligini aytadi). Biz uni o'qib, kechikishni hech bo'lmaganda shu qiymatga ko'taramiz. Bu β eng to'g'ri xulq, chunki server aynan qancha kutishni o'zi aytadi.urinish === maxRetriesβ urinishlar tugaganda oxirgi xatonithrowqilamiz. Cheksiz tsikl yo'q.
Eslatma β SDK retry'i bilan ikki marta urinmang. Agar
withRetryishlatsangiz, klientdamaxRetries: 0qo'ying. Aks holda SDK ham, sizning kodingiz ham qayta urinadi β kechikishlar ko'payadi va sonlar chalkashadi.
5. Timeout β javob juda uzoq kelmasa¶
Ba'zida xato umuman qaytmaydi β chaqiruv shunchaki osilib qoladi. Buning oldini olish uchun timeout (vaqt chegarasi) qo'yiladi: agar javob belgilangan vaqtda kelmasa, SDK chaqiruvni bekor qiladi.
Standart timeout β 10 daqiqa. Uni qisqartirish mumkin:
Timeout sodir bo'lganda SDK Anthropic.APIConnectionTimeoutError tashlaydi (u APIConnectionError ning bir turi β ya'ni bizning retryGaArziydimi uni qayta urinishga arziydi deb baholaydi):
try {
const msg = await client
.withOptions({ timeout: 20000 })
.messages.create({
model: "claude-opus-4-8",
max_tokens: 1024,
messages: [{ role: "user", content: "Uzun matn yoz." }],
});
} catch (err) {
if (err instanceof Anthropic.APIConnectionTimeoutError) {
console.error("Chaqiruv juda uzoq cho'zildi (timeout).");
}
}
Muhim bog'liqlik β uzun chiqishda streaming ishlating. Agar uzun javob (katta
max_tokens) kutsangiz, uni bitta uzun so'rovda olish timeout xavfini oshiradi. Buning yechimi β streaming (04-bob). Streaming'da ma'lumot doimiy oqib turgani uchun ulanish "jim" qolmaydi va timeout muammosi yo'q. Amaliy qoida: uzun chiqishdaclient.messages.stream()ni standart qiling.
6. Javob darajasidagi "xatolar" β refusal va max_tokens¶
Hamma muammo throw bo'lmaydi. Ba'zilari muvaffaqiyatli javob ichida keladi β chaqiruv o'tdi (200 OK), lekin javobning stop_reason maydoni nimadir noto'g'ri ketganini bildiradi. Bular istisno (exception) emas, shuning uchun try/catch ularni tutmaydi β siz stop_reason ni o'zingiz tekshirishingiz kerak. (stop_reason haqida to'liq β 03-bob.)
Ikkita muhim holat:
const msg = await client.messages.create({
model: "claude-opus-4-8",
max_tokens: 1024,
messages,
});
if (msg.stop_reason === "refusal") {
// Xavfsizlik sababli rad etish. Bu xato EMAS β model ataylab rad etdi.
// Foydalanuvchiga bildiring. AYNAN shu prompt bilan qayta URINMANG β
// qayta urinish faqat aynan o'sha rad etishni qaytaradi.
console.warn("Model xavfsizlik sababli rad etdi.");
} else if (msg.stop_reason === "max_tokens") {
// Javob kesilgan (truncated) β max_tokens chegarasiga urildi.
// max_tokens ni oshiring yoki streaming ishlating.
console.warn("Javob kesilgan β max_tokens ni oshiring.");
}
stop_reason: "refusal"β model xavfsizlik sababli javob berishdan bosh tortdi. Bu tarmoq nosozligi emas; aynan shu so'rovni qayta yuborish foydasiz (yana rad etadi). Buni foydalanuvchiga tushunarli ko'rsating va boshqa so'rovni kutib oling.stop_reason: "max_tokens"β javob to'liq tugamadi,max_tokenschegarasida kesildi. Yechim:max_tokensni oshiring yoki uzun chiqish uchun streaming'ga o'ting.
Bu ikkalasini retry mantig'idan ajrating. Backoff faqat
throwbo'lgan vaqtinchalik xatolar uchun.refusalvamax_tokensβ muvaffaqiyatli javoblar; ularnistop_reasonorqali alohida boshqaring.
7. Oqim (stream) ichidagi xatolar¶
Streaming uzoq davom etadigan ulanish (04-bob) β uning o'rtasida ham xato bo'lishi mumkin (tarmoq uzildi, server xatosi). Buni ikki yo'l bilan tutamiz.
.on("error", ...) bilan:
const stream = client.messages
.stream({
model: "claude-opus-4-8",
max_tokens: 1024,
messages: [{ role: "user", content: "Uzun hikoya yoz." }],
})
.on("text", (delta) => process.stdout.write(delta))
.on("error", (err) => {
console.error("\n[Oqimda xato]:", err.message);
});
await stream.finalMessage();
for await da β try/catch bilan:
try {
for await (const event of stream) {
if (event.type === "content_block_delta" && event.delta.type === "text_delta") {
process.stdout.write(event.delta.text);
}
}
} catch (err) {
if (err instanceof Anthropic.APIError) {
console.error("\n[Oqim uzildi]:", err.status, err.message);
} else {
throw err;
}
}
β οΈ Diqqat β yarim javob. Oqim o'rtasida xato yuzaga kelsa, bir qism matn allaqachon ekranga chiqib bo'lgan bo'lishi mumkin. Ya'ni foydalanuvchi to'liq bo'lmagan javobni ko'rib turibdi. Buni chiroyli boshqaring: yarim javobni belgilang ("javob to'liq emas"), kerak bo'lsa qaytadan boshlang. Oqimni qayta urinish biroz murakkabroq β chunki retry butun oqimni boshidan boshlaydi, yarmidan emas. Shuning uchun ko'p ilovada oqim uzilganda butun chaqiruvni qaytadan boshlash eng sodda yechim.
8. Ishonchlilik naqshlari¶
Tipli xato + retry β poydevor. Ustiga production ilova quyidagi naqshlarni qo'shadi.
1. Graceful degradation (yumshoq pasayish) β fallback. Agar asosiy yo'l ishlamasa, ilova butunlay yiqilmasin, balki kamroq, ammo ishlaydigan natijaga "pasaysin". Masalan: asosiy model band bo'lsa, boshqa modelga o'ting; yoki shunga o'xshash savol uchun keshlangan (oldin saqlangan) javobni qaytaring. Foydalanuvchi mukammal bo'lmasa-da, biror javob oladi.
2. Foydalanuvchiga tushunarli xabar. Texnik xato matnini ("429 rate_limit_error...") foydalanuvchiga ko'rsatmang. 429 da: "Hozir tizim band, biroz kuting va qayta urinib ko'ring." Bu β yaxshi UX.
3. request_id ni loglash. Har bir javob obyektida _request_id bor (HTTP request-id sarlavhasidan). Xato yuz berganda uni loglang β Anthropic'ga muammo haqida murojaat qilsangiz, bu ID so'rovni topishga yordam beradi. Pastki chiziq (_) bo'lsa-da, bu ommaviy xususiyat:
const msg = await client.messages.create({ /* ... */ });
console.log("Request ID:", msg._request_id); // req_018Ee...
Xatoda ham: tutilgan Anthropic.APIError da err.request_id bo'lishi mumkin β uni log'ga yozing.
4. Circuit breaker (qisqacha g'oya). Agar bir xizmat ketma-ket ko'p marta barbod bo'layotgan bo'lsa, har safar urinaverish foydasiz β faqat yukni oshiradi. "Circuit breaker" (avtomatik o'chirgich, elektrdagi probka kabi) g'oyasi: ketma-ket N marta xatodan keyin "ochiq" holatga o'tib, bir muddat (masalan, 30s) chaqiruvlarni umuman to'xtatadi (yoki darhol fallback'ga yo'naltiradi). Vaqt o'tgach, bitta sinov chaqiruvi yuboradi β agar o'tsa, "yopiq" holatga qaytadi. Bu serverga ham, sizning ilovangizga ham nafas oldiradi. (To'liq implementatsiya bu bobdan tashqarida, lekin g'oyani bilib qo'ying.)
5. Idempotentlik / dedup. Bir xil so'rov ikki marta yuborilib qolsa (foydalanuvchi tugmani ikki marta bossa, retry ustma-ust tushsa), ikki marta token sarflamaslik uchun so'rovga noyob kalit bering va natijani keshlang β bir xil kalitli so'rov ikkinchi marta kelsa, saqlangan natijani qaytaring.
9. Loyiha: ishlab-chiqarishga-tayyor callClaude o'rovi¶
Endi hammasini birlashtiramiz β haqiqiy ilova ishlatishi mumkin bo'lgan bitta funksiya: tipli xato boshqaruvi + backoff (retry-after ga rioya) + timeout + fallback.
// call-claude.js
import Anthropic from "@anthropic-ai/sdk";
const client = new Anthropic({
maxRetries: 0, // o'z retry'imiz ishlaydi
timeout: 30000, // har chaqiruvga 30s
});
const uxla = (ms) => new Promise((r) => setTimeout(r, ms));
function retryGaArziydimi(err) {
if (err instanceof Anthropic.RateLimitError) return true;
if (err instanceof Anthropic.APIConnectionError) return true; // timeout ham shu yerda
if (err instanceof Anthropic.APIError) return err.status >= 500;
return false;
}
/**
* Ishonchli Claude chaqiruvi.
* @param {object} params - messages.create parametrlari (model, messages, ...)
* @param {object} opts - { maxRetries, base, cap, fallbackModel }
*/
async function callClaude(params, opts = {}) {
const {
maxRetries = 4,
base = 1000,
cap = 30000,
fallbackModel = "claude-haiku-4-5", // band bo'lsa arzonroq/yengilroq modelga o'tamiz
} = opts;
let oxirgiXato;
for (let urinish = 0; urinish <= maxRetries; urinish++) {
try {
const msg = await client.messages.create(params);
// Javob darajasidagi holatlarni tekshiramiz (bular throw EMAS)
if (msg.stop_reason === "refusal") {
// Qayta urinish foydasiz β to'g'ridan-to'g'ri qaytaramiz, chaqiruvchi hal qiladi
return { ok: false, reason: "refusal", message: msg };
}
if (msg.stop_reason === "max_tokens") {
console.warn("β οΈ Javob kesilgan (max_tokens). Request:", msg._request_id);
}
return { ok: true, message: msg };
} catch (err) {
oxirgiXato = err;
// 4xx (429'dan tashqari) β darhol to'xta (fail-fast)
if (!retryGaArziydimi(err)) {
console.error("Qayta urinilmaydigan xato:", err.status, err.message);
break;
}
// Urinishlar tugadi β fallback'ga o'tamiz
if (urinish === maxRetries) break;
let kechikish = Math.min(base * 2 ** urinish, cap) + Math.random() * 1000;
if (err instanceof Anthropic.RateLimitError) {
const ra = Number(err.headers?.["retry-after"]);
if (!Number.isNaN(ra) && ra > 0) kechikish = Math.max(kechikish, ra * 1000);
}
console.warn(`Urinish ${urinish + 1} muvaffaqiyatsiz, ${Math.round(kechikish)}ms kutamiz...`);
await uxla(kechikish);
}
}
// Asosiy yo'l barbod bo'ldi β FALLBACK: boshqa model bilan bir marta urinib ko'ramiz
if (fallbackModel && params.model !== fallbackModel) {
console.warn(`Asosiy model ishlamadi β fallback (${fallbackModel}) bilan urinamiz.`);
try {
const msg = await client.messages.create({ ...params, model: fallbackModel });
return { ok: true, message: msg, usedFallback: true };
} catch (err) {
oxirgiXato = err;
}
}
// Hamma yo'l barbod β chiroyli, tushunarli natija qaytaramiz (ilova qulamaydi)
console.error("Hamma urinish barbod. So'nggi xato:", oxirgiXato?.message);
return {
ok: false,
reason: "exhausted",
userMessage: "Hozir tizim band. Iltimos, biroz kuting va qayta urinib ko'ring.",
error: oxirgiXato,
};
}
// --- Foydalanish ---
const natija = await callClaude({
model: "claude-opus-4-8",
max_tokens: 1024,
messages: [{ role: "user", content: "Node.js'da event loop nima?" }],
});
if (natija.ok) {
const matn = natija.message.content.find((b) => b.type === "text")?.text ?? "";
console.log(natija.usedFallback ? "[fallback model]" : "[asosiy model]");
console.log(matn);
} else if (natija.reason === "refusal") {
console.log("Model bu so'rovga javob bera olmaydi.");
} else {
console.log(natija.userMessage); // foydalanuvchiga tushunarli xabar
}
Bu funksiya nima qiladi:
- Tipli xato tarmog'i. 4xx bo'lsa darhol to'xtaydi (fail-fast); 429/5xx/ulanish bo'lsa qayta urinadi.
- Backoff
retry-afterbilan. Eksponensial kutish + jitter, 429 da serverning ko'rsatmasiga rioya. - Timeout. Klientda 30s β osilib qolish yo'q.
- Javob darajasi.
refusalvamax_tokensni alohida boshqaradi. - Fallback. Asosiy model barbod bo'lsa, arzonroq modelga bir marta o'tadi.
- Chiroyli yakun. Hech qachon
throwbilan ilovani qulatmaydi β har doim{ ok, ... }qaytaradi, foydalanuvchi uchun tushunarli xabar bilan.
Bu naqsh sizning real AI ilovangizning negizi β har bir Claude chaqiruvini shu o'rovdan o'tkazsangiz, ilova vaqtinchalik nosozliklarga chidamli (resilient) bo'ladi.
10. Tez-tez uchraydigan xatolar¶
| Xato | Sabab | Yechim |
|---|---|---|
err.message.includes("429") bilan tekshirish |
Matn solishtirish β mo'rt va ishonchsiz | err instanceof Anthropic.RateLimitError |
| 400/401 da qayta urinish | 4xx β sizning aybingiz, takror foydasiz | Faqat 429/5xx/ulanishni qayta uring |
| SDK retry + o'z retry ikkalasi | Ikki marta qayta urinadi, kechikish ko'payadi | withRetry bilan klientda maxRetries: 0 |
refusal da qayta urinish |
Bu xato emas, model ataylab rad etdi | stop_reason ni tekshiring, qayta urmang |
max_tokens ni xato deb tutish |
Bu throw emas, kesilgan javob |
stop_reason === "max_tokens" β oshiring/stream |
| Backoff'da jitter yo'q | Ko'p klient bir vaqtda urinadi (thundering herd) | + Math.random() * 1000 qo'shing |
| Cheksiz retry | maxRetries cheklanmagan |
Urinishlar sonini va cap ni cheklang |
| Oqim o'rtasidagi xato ushlanmadi | .on("error") yoki try/catch yo'q |
Oqimni o'rang; yarim javobni belgilang |
| Uzun chiqishda timeout | Katta max_tokens streamingsiz |
client.messages.stream() ga o'ting |
Xulosa¶
- Production'da chaqiruvlar muqarrar ba'zan barbod bo'ladi (429, tarmoq, 5xx, 529). Ishonchli klient yiqilmaydi β tushunadi, qaror qiladi, chiroyli boshqaradi.
- Xatoni tipli klass bilan tuting:
Anthropic.RateLimitError,AuthenticationError,BadRequestError, ..., va bazaviyAnthropic.APIError(.statusbilan).instanceofishlating β matn solishtirmang. - 4xx (400/401/403/404) β qayta urinmang, tuzating. 429/5xx/529/ulanish β backoff bilan qayta uring.
- SDK buni o'zi qiladi (
maxRetries, def. 2, eksponensial backoff). O'z retry'ingiz faqat maxsus backoff/loglash/navbat/fallback kerak bo'lganda. - Qo'lda backoff:
base * 2^urinish + jitter, 429 daretry-afterga rioya, urinishlar vacapcheklangan, 4xx qayta urinilmaydi. - Timeout ni sozlang; uzun chiqishda streaming (04-bob).
- Javob darajasi:
refusal(rad β qayta urmang) vamax_tokens(kesilgan β oshiring/stream) β bularthrowemas,stop_reasonorqali. - Ishonchlilik: fallback (degradatsiya), foydalanuvchiga tushunarli xabar,
request_idni loglash, circuit breaker g'oyasi.
Mashqlar¶
Mashqlar uchun client ni 02-bobdagidek sozlang (new Anthropic() + .env da ANTHROPIC_API_KEY). Tokenni isrof qilmaslik uchun kichik max_tokens (masalan 256) bilan boshlang.
Oson¶
-
try/catchyozing vaAnthropic.AuthenticationErrorni alohida tuting. Uni qo'zg'atish uchun klientni ataylab noto'g'ri kalit bilan yarating:new Anthropic({ apiKey: "xato-kalit" })va chaqiruv qiling. Xato klassiniconsole.log(err.constructor.name)bilan chop eting. -
Bir funksiya
retryMumkinmi(err)yozing β uAnthropic.RateLimitError,APIConnectionErrorva 5xx largatrue, qolganlarigafalseqaytarsin. Uni har xil soxta xato obyektlari bilan sinab ko'ring. -
Klientni
new Anthropic({ maxRetries: 5, timeout: 15000 })bilan sozlang va oddiy chaqiruv qiling. SDK endi 429/5xx ni 5 marta o'zi qayta urinishini tushuntiring (kod izohida). -
Bir chaqiruvdan keyin
msg._request_idni chop eting. Nega uni loglash foydali ekanini bir jumlada izohlang.
O'rta¶
-
4-bo'limdagi
withRetryfunksiyasini yozing va uni bir chaqiruvga o'rab ishlating.console.warnorqali har urinishdagi kechikishni ko'rsating. -
withRetrygaretry-afterga rioya qo'shilganini tekshiring: soxtaRateLimitError(qo'ldaerr.headers = { "retry-after": "3" }qo'yib) bilan, kechikish kamida 3000ms bo'lishiniconsole.assertbilan tasdiqlang. -
stop_reasonni boshqaruvchi funksiya yozing: umsgni oladi va"end_turn","max_tokens","refusal"uchun har xil xabar qaytaradi.refusalda "qayta urinmang" deb ogohlantirsin. -
Streaming chaqiruvini
.on("error", ...)bilan o'rang. Internetni o'chirib (yoki noto'g'ri kalit bilan) xato chiroyli ushlanishini va yarim javob holatini ko'ring.
Qiyin¶
-
9-bo'limdagi
callClaudeo'rovini yozing va sinang: asosiy model"claude-opus-4-8", fallback"claude-haiku-4-5". Natija{ ok, ... }strukturasini to'g'ri boshqaring. -
callClaudega oddiy circuit breaker qo'shing: modul darajasidaxatolarKetmaKethisoblagichi tuting. Ketma-ket 3 xatodan keyin keyingi 30s ichida chaqiruvni umuman qilmasdan darhol fallback'ga (yoki xatoga) o'ting. Muvaffaqiyatda hisoblagichni nolga tushiring. -
Idempotentlik:
callClaudegakeyparametri qo'shing va natijalarniMapda keshlang. Bir xilkeybilan ikkinchi chaqiruv kelsa, API ga bormasdan keshlangan natijani qaytaring (qo'shimcha token sarflamang). -
SDK ning avtomatik retry'i bilan o'zingiznikini taqqoslang: bir chaqiruvni
new Anthropic({ maxRetries: 3 })bilan, ikkinchisinimaxRetries: 0+withRetry({ maxRetries: 3 })bilan bajaring. Ikkalasi ham 429/5xx da bir xil natija berishini va 400 da darhol to'xtashini tekshiring.
Yechimlar
Yechimlarda umumiy boshlanish shu deb faraz qilinadi:
import Anthropic from "@anthropic-ai/sdk";
const client = new Anthropic(); // ANTHROPIC_API_KEY .env / muhitdan
const uxla = (ms) => new Promise((r) => setTimeout(r, ms));
1.
const xatoKlient = new Anthropic({ apiKey: "xato-kalit" });
try {
await xatoKlient.messages.create({
model: "claude-opus-4-8",
max_tokens: 256,
messages: [{ role: "user", content: "Salom" }],
});
} catch (err) {
if (err instanceof Anthropic.AuthenticationError) {
console.log("Autentifikatsiya xatosi (401):", err.message);
}
console.log("Klass nomi:", err.constructor.name); // AuthenticationError
}
2.
function retryMumkinmi(err) {
if (err instanceof Anthropic.RateLimitError) return true;
if (err instanceof Anthropic.APIConnectionError) return true;
if (err instanceof Anthropic.APIError) return err.status >= 500;
return false;
}
// Sinov: APIError dan soxta obyektlar yasab ko'rsa bo'ladi, lekin eng ishonchli β
// real xatolar bilan. Mantiq: 429/ulanish/5xx β true, 400/401/403/404 β false.
3.
const client = new Anthropic({ maxRetries: 5, timeout: 15000 });
const msg = await client.messages.create({
model: "claude-opus-4-8",
max_tokens: 256,
messages: [{ role: "user", content: "Salom" }],
});
console.log(msg.content);
// SDK endi 429 va 5xx (va ulanish) xatolarini eksponensial backoff bilan
// 5 martagacha O'ZI qayta urinadi β qo'shimcha retry kodi shart emas.
4.
const msg = await client.messages.create({
model: "claude-opus-4-8",
max_tokens: 256,
messages: [{ role: "user", content: "Salom" }],
});
console.log("Request ID:", msg._request_id);
// Foydali: xato/muammoda Anthropic'ga shu ID bilan murojaat qilsangiz,
// ular so'rovni log'larida aniq topa oladi.
5.
function retryGaArziydimi(err) {
if (err instanceof Anthropic.RateLimitError) return true;
if (err instanceof Anthropic.APIConnectionError) return true;
if (err instanceof Anthropic.APIError) return err.status >= 500;
return false;
}
async function withRetry(fn, { maxRetries = 5, base = 1000, cap = 30000 } = {}) {
let oxirgiXato;
for (let urinish = 0; urinish <= maxRetries; urinish++) {
try {
return await fn();
} catch (err) {
oxirgiXato = err;
if (!retryGaArziydimi(err) || urinish === maxRetries) throw err;
let kechikish = Math.min(base * 2 ** urinish, cap) + Math.random() * 1000;
if (err instanceof Anthropic.RateLimitError) {
const ra = Number(err.headers?.["retry-after"]);
if (!Number.isNaN(ra) && ra > 0) kechikish = Math.max(kechikish, ra * 1000);
}
console.warn(`Urinish ${urinish + 1}: ${Math.round(kechikish)}ms kutamiz`);
await uxla(kechikish);
}
}
throw oxirgiXato;
}
const noRetryKlient = new Anthropic({ maxRetries: 0 });
const msg = await withRetry(() =>
noRetryKlient.messages.create({
model: "claude-opus-4-8",
max_tokens: 256,
messages: [{ role: "user", content: "Salom" }],
})
);
console.log(msg.content);
6. retry-after ga rioyani izolyatsiyalab tekshiramiz (faqat hisoblash mantig'i):
function kechikishHisobla(err, urinish, base = 1000, cap = 30000) {
let kechikish = Math.min(base * 2 ** urinish, cap); // jittersiz β tasdiqlash uchun
if (err instanceof Anthropic.RateLimitError) {
const ra = Number(err.headers?.["retry-after"]);
if (!Number.isNaN(ra) && ra > 0) kechikish = Math.max(kechikish, ra * 1000);
}
return kechikish;
}
// Soxta RateLimitError: prototipini RateLimitError ga moslab, headers qo'yamiz
const soxta = Object.create(Anthropic.RateLimitError.prototype);
soxta.headers = { "retry-after": "3" };
const k = kechikishHisobla(soxta, 0); // base=1000 β 1000, lekin retry-after=3000
console.assert(k >= 3000, "retry-after ga rioya qilinmadi!");
console.log("Kechikish:", k, "ms (kamida 3000 bo'lishi kerak)");
7.
function stopReasonBoshqar(msg) {
switch (msg.stop_reason) {
case "end_turn":
return { holat: "ok", matn: msg.content.find((b) => b.type === "text")?.text ?? "" };
case "max_tokens":
return { holat: "kesilgan", xabar: "Javob kesilgan β max_tokens ni oshiring yoki stream." };
case "refusal":
return { holat: "rad", xabar: "Model rad etdi (xavfsizlik). Aynan shu prompt bilan QAYTA URINMANG." };
default:
return { holat: "boshqa", xabar: `stop_reason: ${msg.stop_reason}` };
}
}
const msg = await client.messages.create({
model: "claude-opus-4-8",
max_tokens: 256,
messages: [{ role: "user", content: "Salom" }],
});
console.log(stopReasonBoshqar(msg));
8.
const stream = client.messages
.stream({
model: "claude-opus-4-8",
max_tokens: 256,
messages: [{ role: "user", content: "Uzunroq matn yoz." }],
})
.on("text", (d) => process.stdout.write(d))
.on("error", (err) => {
console.error("\n[Oqimda xato]:", err.message);
console.error("(Yarim javob allaqachon chiqqan bo'lishi mumkin.)");
});
try {
await stream.finalMessage();
} catch (err) {
console.error("[finalMessage xatosi]:", err.message);
}
// Sinash: new Anthropic({ apiKey: "xato" }) bilan oqim oching.
9.
const client = new Anthropic({ maxRetries: 0, timeout: 30000 });
function retryGaArziydimi(err) {
if (err instanceof Anthropic.RateLimitError) return true;
if (err instanceof Anthropic.APIConnectionError) return true;
if (err instanceof Anthropic.APIError) return err.status >= 500;
return false;
}
async function callClaude(params, opts = {}) {
const { maxRetries = 4, base = 1000, cap = 30000, fallbackModel = "claude-haiku-4-5" } = opts;
let oxirgiXato;
for (let urinish = 0; urinish <= maxRetries; urinish++) {
try {
const msg = await client.messages.create(params);
if (msg.stop_reason === "refusal") return { ok: false, reason: "refusal", message: msg };
return { ok: true, message: msg };
} catch (err) {
oxirgiXato = err;
if (!retryGaArziydimi(err)) break;
if (urinish === maxRetries) break;
let k = Math.min(base * 2 ** urinish, cap) + Math.random() * 1000;
if (err instanceof Anthropic.RateLimitError) {
const ra = Number(err.headers?.["retry-after"]);
if (!Number.isNaN(ra) && ra > 0) k = Math.max(k, ra * 1000);
}
await uxla(k);
}
}
if (fallbackModel && params.model !== fallbackModel) {
try {
const msg = await client.messages.create({ ...params, model: fallbackModel });
return { ok: true, message: msg, usedFallback: true };
} catch (err) {
oxirgiXato = err;
}
}
return { ok: false, reason: "exhausted", userMessage: "Tizim band, biroz kuting.", error: oxirgiXato };
}
const r = await callClaude({
model: "claude-opus-4-8",
max_tokens: 256,
messages: [{ role: "user", content: "Salom" }],
});
console.log(r.ok ? r.message.content : r.userMessage ?? r.reason);
10. Circuit breaker β modul darajasidagi holat:
let xatolarKetmaKet = 0;
let ochiqGacha = 0; // shu vaqtgacha "ochiq" (chaqiruv qilmaymiz)
async function callClaudeCB(params, opts = {}) {
const { fallbackModel = "claude-haiku-4-5" } = opts;
// Breaker "ochiq" bo'lsa β API ga bormasdan darhol fallback/xato
if (Date.now() < ochiqGacha) {
console.warn("Circuit OCHIQ β API'ga bormaymiz.");
if (fallbackModel) {
try {
const msg = await client.messages.create({ ...params, model: fallbackModel });
return { ok: true, message: msg, usedFallback: true };
} catch {
/* fallback ham yiqildi */
}
}
return { ok: false, reason: "circuit_open", userMessage: "Tizim band, keyinroq urining." };
}
try {
const msg = await client.messages.create(params);
xatolarKetmaKet = 0; // muvaffaqiyat β hisoblagich nol
return { ok: true, message: msg };
} catch (err) {
xatolarKetmaKet++;
if (xatolarKetmaKet >= 3) {
ochiqGacha = Date.now() + 30000; // 30s ga "ochiq"
console.warn("Ketma-ket 3 xato β circuit 30s ga OCHILDI.");
}
return { ok: false, reason: "error", error: err };
}
}
11. Idempotentlik / dedup keshi:
const kesh = new Map();
async function callClaudeKesh(params, key) {
if (key && kesh.has(key)) {
console.log("[keshdan]");
return kesh.get(key); // API ga bormaymiz β token tejaymiz
}
const msg = await client.messages.create(params);
const natija = { ok: true, message: msg };
if (key) kesh.set(key, natija);
return natija;
}
const a = await callClaudeKesh(
{ model: "claude-opus-4-8", max_tokens: 256, messages: [{ role: "user", content: "2+2?" }] },
"savol-2plus2"
);
const b = await callClaudeKesh({ /* bir xil */ }, "savol-2plus2"); // keshdan, API'siz
console.log(a.message === b.message); // true β aynan o'sha obyekt
12.
// (a) SDK avtomatik retry
const sdkKlient = new Anthropic({ maxRetries: 3 });
const r1 = await sdkKlient.messages.create({
model: "claude-opus-4-8",
max_tokens: 256,
messages: [{ role: "user", content: "Salom" }],
});
// (b) Qo'lda retry (5-yechimadagi withRetry bilan)
const qoYda = new Anthropic({ maxRetries: 0 });
const r2 = await withRetry(() =>
qoYda.messages.create({
model: "claude-opus-4-8",
max_tokens: 256,
messages: [{ role: "user", content: "Salom" }],
}),
{ maxRetries: 3 }
);
// Ikkalasi ham 429/5xx da qayta urinadi va o'tadi.
// 400 (masalan, noto'g'ri model nomi) da ikkalasi ham DARHOL to'xtaydi β
// SDK ham, withRetry ham 4xx ni qayta urinmaydi.
console.log(r1.content, r2.content);
β¬ οΈ Oldingi: 15 β Prompt caching Β· π README Β· Keyingi: 17 β Embeddings va semantik qidiruv β‘οΈ