Kimi K2.7 Code is now on CometAPI — Kimi's most intelligent coding model to date, reliably follows instructions in long contexts and completes programming tasks with a higher success rate. Try it now

Cara men-debug generasi API AI yang gagal

CometAPI
AnnaJun 4, 2026
Cara men-debug generasi API AI yang gagal

Kegagalan API AI berbeda dari kegagalan API biasa. Respons 200 tidak berarti generasi Anda berhasil. Field content bernilai null tidak selalu merupakan error. Dan prompt yang kemarin berhasil bisa gagal hari ini karena penyedia memperbarui kebijakan konten mereka.

Panduan ini membahas cara membaca error API AI, apa arti setiap mode kegagalan, dan bagaimana membangun penanganan error yang memberi tahu Anda apa yang rusak alih-alih sekadar bahwa sesuatu rusak.

Catatan: Nama model seperti gpt-5.4 dan gpt-5.4-mini yang digunakan dalam artikel ini adalah pengidentifikasi platform CometAPI. Model ini hanya berfungsi melalui https://api.cometapi.com/v1 — bukan langsung melalui API OpenAI atau Anthropic. Lihat daftar model lengkap.

Mengapa debug API AI lebih sulit daripada debug API biasa

Dengan REST API tipikal, 200 berarti sukses dan 4xx berarti Anda melakukan kesalahan. API AI menambahkan kategori ketiga: kegagalan lunak — respons yang mengembalikan 200 tetapi tidak berisi konten yang dapat digunakan.

Tiga hal yang bisa salah:

  1. Kegagalan keras — error HTTP (4xx, 5xx). Permintaan tidak selesai.
  2. Kegagalan lunak — HTTP 200, tetapi finish_reason adalah content_filter atau length, atau content adalah null.
  3. Kegagalan senyap — HTTP 200, konten terlihat baik, tetapi output salah dengan cara yang hanya bisa Anda tangkap di lapisan aplikasi.

Sebagian besar penanganan error hanya mencakup kasus 1. Kasus 2 dan 3 adalah tempat sebagian besar bug produksi berada.

Pahami format respons error

Endpoint text completions mengembalikan struktur error yang konsisten:

{  "error": {    "message": "human-readable description (often includes request id)",    "type": "comet_api_error",    "param": "the_problematic_parameter_or_null",    "code": "error_code_or_null"  }}

Endpoint gambar dan video mengembalikan format error yang berbeda — selalu parsing body respons mentah alih-alih mengasumsikan struktur tetap di seluruh endpoint.

Field message biasanya memberi tahu Anda persis apa yang salah. Field param memberi tahu Anda parameter mana yang menyebabkannya. Selalu log keduanya.

Ketahui arti setiap kode status HTTP

StatusArtiPenyebab umumPerbaikan
400Bad requestModel hilang, parameter salah untuk model iniPeriksa error.param dalam respons
401UnauthorizedAPI key salah atau tidak adaVerifikasi format Authorization: Bearer
429Rate limitedTerlalu banyak permintaanExponential backoff (lihat Langkah 4)
500Server errorMasalah di penyedia, atau body permintaan salahRetry dengan backoff; periksa format request
504Gateway timeoutPenyedia terlalu lama meresponsRetry; pertimbangkan model yang lebih cepat

Sumber**: CometAPI chat completions docs

Pembedaan 400 vs 500 penting untuk logika retry. 400 berarti permintaan Anda salah — mengulang permintaan yang sama tidak akan membantu. 500 atau 504 berarti server bermasalah — retry masuk akal.

Periksa finish_reason — field yang paling sering diabaikan

Respons 200 dengan finish_reason: "content_filter" berarti generasi Anda diblokir. Field content akan null atau kosong. Jika Anda tidak memeriksa ini, aplikasi Anda akan mengembalikan tidak ada apa-apa tanpa peringatan.

finish_reasonArtiApa yang harus dilakukanPerbaikan
stopSelesai normalTidak ada — ini suksesPeriksa error.param dalam respons
lengthMencapai batas tokenTingkatkan max_tokens atau persingkat promptVerifikasi format Authorization: Bearer
content_filterDiblokir oleh kebijakan keamananUbah redaksi prompt; hindari nama/topik tertentuExponential backoff (lihat Langkah 4)
tool_callsModel memanggil tool alih-alih mengembalikan teksTangani pemanggilan tool; content akan nullRetry dengan backoff; periksa format request
504Gateway timeoutPenyedia terlalu lama meresponsRetry; pertimbangkan model yang lebih cepat
import osimport loggingfrom openai import OpenAI, APIStatusError, APIConnectionError, APITimeoutErrorfrom dotenv import load_dotenv​load_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"])

Deteksi kegagalan senyap di lapisan aplikasi

Kegagalan senyap adalah yang paling sulit ditangkap. API mengembalikan 200, finish_reason adalah stop, tetapi output secara semantik salah. Anda hanya dapat menangkap ini di lapisan aplikasi.

Pola umum:

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")

Kegagalan senyap biasanya berasal dari salah satu dari tiga sumber: prompt ambigu, model mengabaikan instruksi format Anda, atau input terlalu pendek/panjang untuk tugasnya. Melog seluruh output saat validasi gagal adalah cara tercepat untuk mendiagnosis yang mana.

Tambahkan exponential backoff untuk rate limit

Error rate limit (429) bersifat sementara. Respons yang tepat adalah menunggu dan retry dengan jeda yang meningkat — praktik standar untuk API apa pun dengan rate limit:

import timeimport randomfrom openai import RateLimitError​def 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

Jangan retry pada 400 atau 401 — itu error sisi klien yang tidak akan teratasi dengan sendirinya.

Debug kegagalan pembuatan gambar

Pembuatan gambar punya mode kegagalan sendiri di atas error HTTP standar:

import base64import requests​def 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    }

Masalah khusus gambar yang perlu diwaspadai:

GejalaPenyebabPerbaikan
Array data kosongPrompt difilterPeriksa revised_prompt; ubah redaksi
error response_format pada GPT Image 2Parameter tidak didukungGunakan output_format sebagai ganti
n > 1 error pada Qwen ImageLimitasi modelLakukan loop permintaan sebagai gantinya
URL mengembalikan 403 kemudianURL kedaluwarsaUnduh segera setelah pembuatan

Sumber**: CometAPI image generation docs

Debug kegagalan pembuatan video

Pembuatan video gagal dengan cara berbeda karena bersifat async. Inisialisasi variabel status sebelum loop agar pesan error timeout selalu terbentuk dengan baik:

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}%"    )

Masalah khusus video:

GejalaPenyebabPerbaikan
Tugas macet di queued > 10 menitBeban serverRetry dengan model berbeda
failed tanpa detail errorPrompt difilter atau error modelUbah redaksi prompt
URL video mengembalikan 403URL kedaluwarsaUnduh segera
task_not_exist pada poll pertama RunwayTugas masih inisialisasi (perilaku terdokumentasi CometAPI)Tunggu 5 dtk lalu retry
Kling mengembalikan "succeed" bukan "succeeded"API Kling memakai status non-standarTangani keduanya dalam polling

Sumber**: CometAPI video generation docs**, Kling Video docs

Versi Node.js

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);}

Daftar periksa debugging

Saat sebuah generasi gagal dan Anda tidak yakin harus mulai dari mana:

Untuk pembuatan teks:

  • Apakah API key disetel dan dalam format Authorization: Bearer <key>?
  • Apakah finish_reason selain stop?
  • Apakah content null? Periksa apakah finish_reason adalah tool_calls
  • Apakah output terpotong? Periksa finish_reason: "length" dan usage.completion_tokens
  • Apakah error-nya 4xx (perbaiki request) atau 5xx (retry)?
  • Apakah output lulus validasi lapisan aplikasi Anda? (kegagalan senyap)

Untuk pembuatan gambar:

  • Apakah array data kosong? (content filter)
  • Apakah Anda menggunakan response_format pada GPT Image 2? (tidak didukung — gunakan output_format)
  • Apakah Anda menyetel n > 1 pada Qwen Image? (tidak didukung)
  • Apakah Anda mengunduh gambar sebelum URL kedaluwarsa?

Untuk pembuatan video:

  • Apakah tugas macet di queued? (coba model berbeda)
  • Apakah Anda memeriksa field error pada respons tugas yang gagal?
  • Apakah Anda mengunduh video sebelum URL kedaluwarsa?
  • Apakah Anda menangani "succeed" (Kling) dan "succeeded" (Veo, Runway)?

FAQ

Q: Permintaan saya mengembalikan 200 tetapi tidak ada konten. Apa yang terjadi?

Periksa finish_reason. Jika content_filter, generasi diblokir — permintaan berhasil tetapi output disembunyikan. Jika tool_calls, model memanggil tool alih-alih mengembalikan teks, dan content null sesuai desain. Jika finish_reason adalah stop tetapi konten tetap kosong, itu kegagalan senyap — log seluruh respons dan periksa prompt Anda.

Q: Bagaimana saya tahu jika prompt saya sedang difilter?

Untuk teks: periksa finish_reason === "content_filter". Untuk gambar: periksa apakah array data kosong. Untuk video: periksa apakah tugas mencapai status failed segera setelah pengiriman tanpa detail error. Dalam semua kasus, coba ubah redaksi prompt menjadi lebih netral.

Q: Kapan saya harus retry permintaan yang gagal?

Retry pada 429 dan 5xx menggunakan exponential backoff. Jangan retry pada 4xx — bad request tidak akan membaik dengan sendirinya. Pengecualian adalah 401 jika Anda memutar API key.

Q: Apa itu exponential backoff dan mengapa penting?

Alih-alih retry segera, Anda menunggu semakin lama: 1 dtk, 2 dtk, 4 dtk. Menambahkan jitter acak (+ random.random()) mencegah banyak klien melakukan retry secara serempak. Ini praktik standar untuk API apa pun dengan rate limit — bukan khusus CometAPI.

Q: Tugas video macet di queued selama 10 menit. Apakah gagal?

Belum tentu — antrean bisa menumpuk saat beban tinggi. Tunggu hingga ambang max_wait Anda, lalu naikkan TimeoutError dan retry dengan model berbeda. Log ID tugas agar Anda dapat memeriksa status secara manual jika diperlukan.

Q: Bagaimana cara menangkap kegagalan senyap?

Kegagalan senyap memerlukan validasi di lapisan aplikasi — API tidak akan memberi tahu bahwa output salah secara semantik. Periksa apakah output sesuai format yang diharapkan (JSON valid, label yang diharapkan, panjang minimum). Log seluruh output saat validasi gagal. Penyebab paling umum adalah prompt ambigu, instruksi format diabaikan, atau input yang terlalu pendek atau terlalu panjang untuk tugasnya.

Siap memangkas biaya pengembangan AI hingga 20%?

Mulai gratis dalam beberapa menit. Kredit uji coba gratis disertakan. Tidak perlu kartu kredit.

Baca Selengkapnya