Yapay zeka API başarısızlıkları, normal API başarısızlıklarından farklıdır. 200 yanıtı, üretiminizin başarılı olduğu anlamına gelmez. null content alanı her zaman bir hata değildir. Ve dün çalışan aynı istem, bir sağlayıcı içerik politikasını güncellediği için bugün başarısız olabilir.
Bu kılavuz, yapay zeka API hatalarını nasıl okuyacağınızı, her bir başarısızlık modunun gerçekte ne anlama geldiğini ve “bir şeyler bozuldu” demek yerine “neyin bozulduğunu” söyleyen hata yakalama mantığını nasıl kuracağınızı kapsar.
Not: Bu yazıda kullanılan gpt-5.4 ve gpt-5.4-mini gibi model adları CometAPI’nin platform tanımlayıcılarıdır. Yalnızca https://api.cometapi.com/v1 üzerinden çalışırlar — OpenAI veya Anthropic’in API’leri üzerinden doğrudan çalışmazlar. tam model listesine bakın.
Yapay zeka API hata ayıklaması neden normal API hata ayıklamasından daha zordur
Tipik bir REST API’de 200 başarı, 4xx ise sizin bir şeyi yanlış yaptığınız anlamına gelir. Yapay zeka API’leri üçüncü bir kategori ekler: 200 dönen fakat kullanılabilir içerik içermeyen yanıtlar — yani yumuşak başarısızlıklar.
Üç şey ters gidebilir:
- Ciddi başarısızlık — HTTP hatası (4xx, 5xx). İstek tamamlanmadı.
- Yumuşak başarısızlık — HTTP 200, ancak
finish_reasoncontent_filterveyalength, ya dacontentnull. - Sessiz başarısızlık — HTTP 200, içerik düzgün görünüyor, ancak çıktı sadece uygulama katmanında yakalayabileceğiniz şekilde hatalı.
Hata yakalama mantığının çoğu yalnızca 1. durumu kapsar. 2 ve 3. durumlar ise üretimdeki hataların çoğunun yaşandığı yerlerdir.
Hata yanıtı biçimini anlayın
Metin tamamlama uç noktası tutarlı bir hata yapısı döndürür:
{ "error": { "message": "human-readable description (often includes request id)", "type": "comet_api_error", "param": "the_problematic_parameter_or_null", "code": "error_code_or_null" }}
Görüntü ve video uç noktaları farklı hata biçimleri döndürür — uç noktalar arasında sabit bir yapıyı varsaymak yerine her zaman ham yanıt gövdesini ayrıştırın.
message alanı genellikle sorunun ne olduğunu açıkça söyler. param alanı hangi parametrenin buna sebep olduğunu belirtir. Her ikisini de mutlaka kaydedin.
Her HTTP durum kodunun ne anlama geldiğini bilin
| Durum | Anlamı | Yaygın neden | Çözüm |
|---|---|---|---|
| 400 | Kötü istek | Eksik model, bu model için yanlış parametre | Yanttaki error.param değerini kontrol edin |
| 401 | Yetkisiz | Hatalı veya eksik API anahtarı | Authorization: Bearer |
| 429 | Oran sınırı | Çok fazla istek | Üssel geri çekilme (bkz. Adım 4) |
| 500 | Sunucu hatası | Sağlayıcı tarafı sorun veya hatalı istek gövdesi | Geri çekilerek yeniden deneyin; istek biçimini kontrol edin |
| 504 | Ağ geçidi zaman aşımı | Sağlayıcı çok uzun sürdü | Yeniden deneyin; daha hızlı bir model düşünün |
Kaynak**:* CometAPI chat completions docs
Yeniden deneme mantığı için 400 ile 500 ayrımı önemlidir. 400 isteğinizin hatalı olduğunu ifade eder — aynı isteği yeniden denemek yardımcı olmaz. 500 veya 504 sunucunun bir sorunu olduğu anlamına gelir — yeniden denemek mantıklıdır.
finish_reason alanını kontrol edin — en gözden kaçan alan
finish_reason: "content_filter" ile gelen bir 200 yanıtı, üretiminizin engellendiği anlamına gelir. content alanı null ya da boştur. Bunu kontrol etmezseniz, uygulamanız sessizce hiçbir şey döndürür.
| finish_reason | Anlamı | Ne yapmalı | Çözüm |
|---|---|---|---|
| stop | Normal tamamlama | Gerek yok — bu başarı | Yanttaki error.param değerini kontrol edin |
| length | Token sınırına ulaşıldı | max_tokens'ı artırın veya istemi kısaltın | Authorization: Bearer |
| content_filter | Güvenlik politikası tarafından engellendi | İstemi yeniden ifade edin; belirli isim/konulardan kaçının | Üssel geri çekilme (bkz. Adım 4) |
| tool_calls | Model metin dönmek yerine bir araç çağırdı | Araç çağrısını işleyin; content null olacaktır | Geri çekilerek yeniden deneyin; istek biçimini kontrol edin |
| 504 | Ağ geçidi zaman aşımı | Sağlayıcı çok uzun sürdü | Yeniden deneyin; daha hızlı bir model düşünün |
Kaynak**:* CometAPI chat completions docs
import osimport loggingfrom openai import OpenAI, APIStatusError, APIConnectionError, APITimeoutErrorfrom dotenv import load_dotenvload_dotenv()api_key = os.environ.get("COMETAPI_KEY")if not api_key: raise ValueError("COMETAPI_KEY is not set")client = OpenAI( base_url="https://api.cometapi.com/v1", api_key=api_key,)def safe_complete(messages: list, model: str = "gpt-5.4-mini", **kwargs) -> dict: """ Complete a chat request with full error and finish_reason handling. Returns {"content": str, "finish_reason": str, "tool_calls": list | None} Raises on API errors. """ try: response = client.chat.completions.create( model=model, messages=messages, **kwargs ) except APIStatusError as e: error_body = {} try: error_body = e.response.json().get("error", {}) except Exception: pass logging.error( f"API error status={e.status_code} " f"message={error_body.get('message')} " f"param={error_body.get('param')}" ) raise except (APIConnectionError, APITimeoutError) as e: logging.error(f"Network/timeout error: {e}") raise choice = response.choices[0] finish_reason = choice.finish_reason if finish_reason == "content_filter": raise ValueError( f"Generation blocked by content filter. " f"Model: {model}. Rephrase the prompt." ) if finish_reason == "length": used = response.usage.completion_tokens if response.usage else "unknown" logging.warning(f"Output truncated at token limit. Used {used} tokens.") # Return structured result so callers can handle tool_calls explicitly return { "content": choice.message.content or "", "finish_reason": finish_reason, "tool_calls": choice.message.tool_calls, }# Usageresult = safe_complete( messages=[{"role": "user", "content": "Summarize this article: [text]"}], model="gpt-5.4-mini")if result["finish_reason"] == "tool_calls": # Handle tool call — content will be empty print("Model wants to call a tool:", result["tool_calls"])else: print(result["content"])
Sessiz başarısızlıkları uygulama katmanında tespit edin
Sessiz başarısızlıklar yakalaması en zor olanlardır. API 200 döndürür, finish_reason stop olur, ancak çıktı anlamsal olarak yanlıştır. Bunları yalnızca uygulama katmanında yakalayabilirsiniz.
Yaygın kalıplar:
def validate_completion(result: dict, task: str) -> str: """ Application-layer validation for silent failures. Raises ValueError if the output doesn't meet basic expectations. """ content = result["content"].strip() # Empty output that isn't a tool call if not content and result["finish_reason"] != "tool_calls": raise ValueError(f"Empty output for task '{task}' with finish_reason='{result['finish_reason']}'") # Task-specific checks if task == "classify": valid_labels = {"positive", "negative", "neutral"} if content.lower() not in valid_labels: logging.warning( f"Unexpected classification output: '{content}'. " f"Expected one of {valid_labels}. " f"Model may have returned explanation instead of label." ) if task == "json_extract": import json try: json.loads(content) except json.JSONDecodeError: raise ValueError( f"Expected JSON output but got: '{content[:100]}...'. " f"Try adding 'respond with valid JSON only' to the prompt, " f"or use response_format={{\"type\": \"json_object\"}}." ) if task == "summarize" and len(content.split()) < 10: logging.warning( f"Suspiciously short summary ({len(content.split())} words). " f"Check if the input was too short or the model misunderstood the task." ) return content# Full flow with silent failure detectionresult = safe_complete( messages=[{"role": "user", "content": "Classify as positive/negative/neutral: 'Great product!'"}], model="claude-haiku-4-5")label = validate_completion(result, task="classify")
Sessiz başarısızlıklar genellikle üç kaynaktan birinden gelir: istem belirsizdir, model biçim talimatlarınızı yok saymıştır veya girdi görev için çok kısa/uzundur. Doğru olanı teşhis etmenin en hızlı yolu, doğrulama başarısız olduğunda tam çıktıyı günlüğe kaydetmektir.
Oran sınırlamaları için üssel geri çekilme ekleyin
Oran sınırlaması hataları (429) geçicidir. Doğru yaklaşım, artan gecikmelerle beklemek ve yeniden denemektir — oran sınırı olan herhangi bir API için standart bir uygulama:
import timeimport randomfrom openai import RateLimitErrordef complete_with_retry( messages: list, model: str = "gpt-5.4-mini", max_retries: int = 3, **kwargs) -> dict: """Retry on rate limits and server errors with exponential backoff.""" last_error = None for attempt in range(max_retries): try: return safe_complete(messages, model=model, **kwargs) except APIStatusError as e: if e.status_code < 500: raise # 4xx: don't retry, request is wrong last_error = e except RateLimitError as e: last_error = e except (APIConnectionError, APITimeoutError) as e: last_error = e if attempt < max_retries - 1: wait = (2 ** attempt) + random.random() # jitter prevents thundering herd logging.warning(f"Attempt {attempt + 1} failed. Waiting {wait:.1f}s before retry.") time.sleep(wait) raise RuntimeError(f"All {max_retries} attempts failed") from last_error
400 veya 401’de yeniden denemeyin — bunlar kendiliğinden düzelmeyecek istemci hatalarıdır.
Görüntü üretimi hatalarını ayıklayın
Görüntü üretimi, standart HTTP hatalarının üzerine kendine özgü başarısızlık modlarına sahiptir:
import base64import requestsdef generate_image_safe(prompt: str, model: str = "dall-e-3") -> dict: """ Generate an image with full error handling. Returns {"url": str | None, "bytes": bytes | None, "blocked": bool} """ api_key = os.environ.get("COMETAPI_KEY") if not api_key: raise ValueError("COMETAPI_KEY is not set") BASE64_MODELS = {"gpt-image-2", "qwen-image"} headers = { "Authorization": f"Bearer {api_key}", "Content-Type": "application/json" } payload = {"model": model, "prompt": prompt, "size": "1024x1024"} if model in BASE64_MODELS: payload["output_format"] = "png" else: payload["response_format"] = "url" try: response = requests.post( "https://api.cometapi.com/v1/images/generations", json=payload, headers=headers, timeout=60 ) response.raise_for_status() except requests.exceptions.HTTPError as e: logging.error(f"Image generation HTTP error: {e.response.status_code} {e.response.text}") raise except requests.exceptions.Timeout: logging.error("Image generation timed out after 60s") raise data = response.json().get("data", []) if not data: logging.warning("Image generation returned empty data — prompt may have been filtered.") return {"url": None, "bytes": None, "blocked": True} item = data[0] if "revised_prompt" in item: logging.info(f"Provider revised prompt to: {item['revised_prompt']}") if "url" in item: return {"url": item["url"], "bytes": None, "blocked": False} return { "url": None, "bytes": base64.b64decode(item["b64_json"]), "blocked": False }
Görüntüye özgü dikkat edilmesi gerekenler:
| Belirti | Neden | Çözüm |
|---|---|---|
| Boş data dizisi | İstem filtrelendi | revised_prompt’u kontrol edin; yeniden ifade edin |
| GPT Image 2’de response_format hatası | Parametre desteklenmiyor | Bunun yerine output_format kullanın |
| Qwen Image’da n > 1 hatası | Model kısıtı | Bunun yerine istekleri döngüye alın |
| URL daha sonra 403 döndürüyor | URL’nin süresi doldu | Üretimden hemen sonra indirin |
Kaynak**:* CometAPI image generation docs
Video üretimi hatalarını ayıklayın
Video üretimi asenkron olduğu için farklı şekilde başarısız olur. Zaman aşımı hata mesajı her zaman düzgün biçimde olsun diye döngüden önce durum değişkenlerini başlatın:
def submit_and_poll_video( prompt: str, model: str = "veo3-fast", max_wait: int = 600) -> str: """Submit video task and poll to completion. Returns video URL.""" api_key = os.environ.get("COMETAPI_KEY") if not api_key: raise ValueError("COMETAPI_KEY is not set") headers = {"Authorization": f"Bearer {api_key}"} try: response = requests.post( "https://api.cometapi.com/v1/videos", headers=headers, files={ "prompt": (None, prompt), "model": (None, model), "size": (None, "16x9") }, timeout=30 ) response.raise_for_status() except requests.exceptions.HTTPError as e: logging.error(f"Video submit failed: {e.response.status_code} {e.response.text}") raise task_id = response.json()["id"] logging.info(f"Video task submitted: {task_id}") poll_url = f"https://api.cometapi.com/v1/videos/{task_id}" elapsed = 0 interval = 10 status = "unknown" # initialize before loop progress = 0 # initialize before loop while elapsed < max_wait: try: poll_response = requests.get(poll_url, headers=headers, timeout=30) poll_response.raise_for_status() except requests.exceptions.HTTPError as e: logging.error(f"Poll request failed: {e.response.status_code}") raise result = poll_response.json() status = result.get("status", "unknown") progress = result.get("progress", 0) logging.info(f"Task {task_id}: status={status} progress={progress}%") if status == "succeeded": return result["output"][0] elif status in ("failed", "cancelled"): error_detail = result.get("error", "no error detail returned") raise RuntimeError(f"Video task {task_id} failed: {error_detail}") time.sleep(interval) elapsed += interval raise TimeoutError( f"Video task {task_id} did not complete within {max_wait}s. " f"Last status: {status}, progress: {progress}%" )
Videoya özgü sorunlar:
| Belirti | Neden | Çözüm |
|---|---|---|
| Görev 10+ dk boyunca queued’da takılı | Sunucu yükü | Farklı bir modelle yeniden deneyin |
| Herhangi bir hata detayı olmadan failed | İstem filtrelendi veya model hatası | İstemi yeniden ifade edin |
| Video URL’si 403 döndürüyor | URL’nin süresi doldu | Hemen indirin |
| Runway’de ilk sorguda task_not_exist | Görev hâlâ başlatılıyor (CometAPI tarafından belgelenmiş davranış) | 5 sn bekleyip yeniden deneyin |
| Kling “succeeded” yerine “succeed” döndürür | Kling’in API’si standart dışı durum dizesi kullanır | Yoklamada her ikisini de ele alın |
Kaynak**:* CometAPI video generation docs**,* Kling Video docs
Node.js sürümü
import OpenAI from 'openai';const apiKey = process.env.COMETAPI_KEY;if (!apiKey) throw new Error('COMETAPI_KEY is not set');const client = new OpenAI({ baseURL: 'https://api.cometapi.com/v1', apiKey,});async function safeComplete(messages, model = 'gpt-5.4-mini', options = {}) { let response; try { response = await client.chat.completions.create({ model, messages, ...options }); } catch (err) { if (err.status && err.status < 500) { console.error(`Client error ${err.status}: ${err.message}`); } else { console.error(`Server/network error: ${err.message}`); } throw err; } const choice = response.choices[0]; const finishReason = choice.finish_reason; if (finishReason === 'content_filter') { throw new Error(`Generation blocked by content filter. Model: ${model}`); } if (finishReason === 'length') { console.warn(`Output truncated. Used ${response.usage?.completion_tokens ?? 'unknown'} tokens.`); } return { content: choice.message.content ?? '', finishReason, toolCalls: choice.message.tool_calls ?? null, };}async function completeWithRetry(messages, model = 'gpt-5.4-mini', maxRetries = 3) { let lastError; for (let attempt = 0; attempt < maxRetries; attempt++) { try { return await safeComplete(messages, model); } catch (err) { // Don't retry 4xx client errors if (err.status && err.status < 500) throw err; lastError = err; if (attempt < maxRetries - 1) { const wait = (2 ** attempt + Math.random()) * 1000; console.warn(`Attempt ${attempt + 1} failed. Retrying in ${(wait / 1000).toFixed(1)}s`); await new Promise(r => setTimeout(r, wait)); } } } throw new Error(`All ${maxRetries} attempts failed: ${lastError?.message}`);}// Usageconst result = await safeComplete( [{ role: 'user', content: 'Classify as positive/negative/neutral: "Great product!"' }], 'claude-haiku-4-5');if (result.finishReason === 'tool_calls') { console.log('Tool call requested:', result.toolCalls);} else { console.log(result.content);}
Bir hata ayıklama kontrol listesi
Bir üretim başarısız olduğunda ve nereden başlayacağınızdan emin olmadığınızda:
Metin üretimi için:
- API anahtarı ayarlı mı ve
Authorization: Bearer <key>biçiminde mi? finish_reasonstopdışında bir şey mi?contentnull mı?finish_reasontool_callsmı kontrol edin- Çıktı kesildi mi?
finish_reason: "length"veusage.completion_tokens’ı kontrol edin - Hata 4xx mi (isteği düzeltin) yoksa 5xx mi (yeniden deneyin)?
- Çıktı uygulama katmanı doğrulamanızdan geçiyor mu? (sessiz başarısızlık)
Görüntü üretimi için:
datadizisi boş mu? (içerik filtresi)- GPT Image 2’de
response_formatmi kullandınız? (desteklenmiyor —output_formatkullanın) - Qwen Image’da
n > 1mi ayarladınız? (desteklenmiyor) - URL’nin süresi dolmadan önce görüntüyü indirdiniz mi?
Video üretimi için:
- Görev
queueddurumunda mı takıldı? (farklı bir modeli deneyin) - Başarısız görev yanıtındaki
erroralanını kontrol ettiniz mi? - Videoyu URL’nin süresi dolmadan önce indirdiniz mi?
- Hem
"succeed"(Kling) hem de"succeeded"(Veo, Runway) durumlarını ele alıyor musunuz?
SSS
S: İsteğim 200 döndürüyor ama içerik yok. Ne oldu?
finish_reason’ı kontrol edin. content_filter ise üretim engellenmiştir — istek başarılı oldu ama çıktı bastırıldı. tool_calls ise model metin yerine bir araç çağırdı ve content tasarım gereği null’dır. finish_reason stop ama içerik yine de boşsa bu bir sessiz başarısızlıktır — tam yanıtı kaydedin ve isteminizi kontrol edin.
S: İstemimin filtrelenip filtrelenmediğini nasıl anlarım?
Metin için: finish_reason === "content_filter"’ı kontrol edin. Görüntü için: data dizisinin boş olup olmadığına bakın. Video için: görev gönderimden kısa süre sonra failed durumuna düşüyor mu ve hata detayı yok mu kontrol edin. Her durumda, istemi daha nötr olacak şekilde yeniden ifade etmeyi deneyin.
S: Başarısız bir isteği ne zaman yeniden denemeliyim?
429 ve 5xx’lerde üssel geri çekilmeyle yeniden deneyin. 4xx’lerde yeniden denemeyin — hatalı bir istek kendiliğinden düzelmez. İstisna, API anahtarlarını döndürüyorsanız 401 olabilir.
S: Üssel geri çekilme nedir ve neden önemlidir?
Hemen yeniden denemek yerine giderek daha uzun beklersiniz: 1 sn, 2 sn, 4 sn. Rasgele titreme (random jitter) eklemek (ör. + random.random()) birden fazla istemcinin aynı anda yeniden denemesini önler. Bu, oran sınırlı her API için standart bir uygulamadır — CometAPI’ye özgü değildir.
S: Video görevi 10 dakika boyunca queued durumunda takılı kaldı. Bu başarısız mı?
Şart değil — yük altında kuyruklar birikebilir. max_wait eşiğinize kadar bekleyin, sonra bir TimeoutError fırlatın ve farklı bir modelle yeniden deneyin. Gerekirse durumu elle kontrol edebilmek için görev kimliğini kaydedin.
S: Sessiz başarısızlıkları nasıl yakalarım?
Sessiz başarısızlıklar uygulama katmanı doğrulaması gerektirir — API çıktının anlamsal olarak yanlış olduğunu söylemez. Çıktının beklenen biçime uyduğunu kontrol edin (geçerli JSON, beklenen etiket, asgari uzunluk). Doğrulama başarısız olduğunda tam çıktıyı kaydedin. En yaygın nedenler belirsiz istemler, yok sayılan biçim talimatları veya görev için çok kısa/çok uzun girdilerdir.
