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

Sådan fejlsøger du mislykkede AI API-genereringer

CometAPI
AnnaJun 4, 2026
Sådan fejlsøger du mislykkede AI API-genereringer

AI API-fejl er anderledes end almindelige API-fejl. Et 200-svar betyder ikke, at din generering lykkedes. Et null content-felt er ikke altid en fejl. Og den samme prompt, der virkede i går, kan fejle i dag, fordi en udbyder opdaterede deres indholdspolitik.

Denne guide beskriver, hvordan du læser AI API-fejl, hvad hver fejlsituation faktisk betyder, og hvordan du bygger fejlhåndtering, der fortæller dig, hvad der gik i stykker i stedet for bare at noget gik i stykker.

Bemærk: Modelnavne som gpt-5.4 og gpt-5.4-mini brugt i denne artikel er CometAPI’s platformidentifikatorer. De virker kun via https://api.cometapi.com/v1 — ikke direkte via OpenAI- eller Anthropics API’er. Se den fulde modelliste.

Hvorfor AI API-fejlsøgning er sværere end almindelig API-fejlsøgning

Med et typisk REST API betyder 200 succes og 4xx betyder, at du gjorde noget forkert. AI API’er tilføjer en tredie kategori: bløde fejl — svar der returnerer 200 men ikke indeholder brugbart indhold.

Tre ting kan gå galt:

  1. Hård fejl — HTTP-fejl (4xx, 5xx). Forespørgslen blev ikke fuldført.
  2. Blød fejl — HTTP 200, men finish_reason er content_filter eller length, eller content er null.
  3. Stiltiende fejl — HTTP 200, indholdet ser fint ud, men outputtet er forkert på en måde, du kun opfanger på applikationslaget.

De fleste fejlhåndteringer dækker kun tilfælde 1. Tilfælde 2 og 3 er der, hvor de fleste produktionsfejl lever.

Forstå formatet for fejlrespons

Tekst-completions-endpointet returnerer en konsistent fejlstruktur:

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

Billed- og video-endpoints returnerer andre fejlformater — parse altid den rå svarbody i stedet for at antage en fast struktur på tværs af endpoints.

Feltet message fortæller dig som regel præcist, hvad der er galt. Feltet param fortæller dig, hvilken parameter der forårsagede det. Log altid begge.

Kend betydningen af hver HTTP-statuskode

StatusBetydningAlmindelig årsagLøsning
400Bad requestManglende model, forkert parameter for denne modelTjek error.param i svaret
401UnauthorizedForkert eller manglende API-nøgleVerificér formatet Authorization: Bearer <key>
429Rate limitedFor mange forespørgslerEksponentiel backoff (se trin 4)
500Server errorProblem hos udbyder eller misformet request-bodyPrøv igen med backoff; tjek request-format
504Gateway timeoutUdbyderen tog for lang tidPrøv igen; overvej en hurtigere model

Kilde: CometAPI chat completions dokumentation

Skelnen mellem 400 og 500 er vigtig for retry-logik. En 400 betyder, at din forespørgsel er forkert — at prøve igen med den samme forespørgsel hjælper ikke. En 500 eller 504 betyder, at serveren havde et problem — her giver det mening at prøve igen.

Tjek finish_reason — det mest oversete felt

Et 200-svar med finish_reason: "content_filter" betyder, at din generering blev blokeret. Feltet content vil være null eller tomt. Hvis du ikke tjekker dette, returnerer din app stiltiende ingenting.

finish_reasonBetydningHvad skal du gøreLøsning
stopNormal afslutningIntet — dette er succesTjek error.param i svaret
lengthRamt token-grænseØg max_tokens eller forkort promptenVerificér formatet Authorization: Bearer <key>
content_filterBlokeret af sikkerhedspolitikOmformuler prompten; undgå specifikke navne/emnerEksponentiel backoff (se trin 4)
tool_callsModellen kaldte et værktøj i stedet for at returnere tekstHåndter værktøjskaldet; content vil være nullPrøv igen med backoff; tjek request-format
504Gateway timeoutUdbyderen tog for lang tidPrøv igen; overvej en hurtigere model

Kilde: CometAPI chat completions dokumentation

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

Opfang stiltiende fejl på applikationslaget

Stiltiende fejl er de sværeste at fange. API’et returnerer 200, finish_reason er stop, men outputtet er semantisk forkert. Du kan kun fange disse på applikationslaget.

Almindelige mønstre:

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

Stiltiende fejl kommer typisk fra én af tre kilder: prompten er tvetydig, modellen ignorerede dine formatinstruktioner, eller inputtet var for kort/langt til opgaven. Log det fulde output, når valideringen fejler — det er den hurtigste måde at diagnosticere, hvilken det er.

Tilføj eksponentiel backoff for rate limits

Rate limit-fejl (429) er midlertidige. Den rigtige reaktion er at vente og prøve igen med stigende forsinkelser — en standardpraksis for ethvert API med rate limits:

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

Forsøg ikke igen på 400 eller 401 — det er klientfejl, som ikke løser sig selv.

Fejlsøg fejl i billedgenerering

Billedgenerering har sine egne fejlsituationer oven på de standard HTTP-fejl:

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    }

Billedspecifikke problemer at holde øje med:

SymptomÅrsagLøsning
Tom data-arrayPrompt filtreretTjek revised_prompt; omformuler
response_format-fejl på GPT Image 2Parameter ikke understøttetBrug output_format i stedet
n > 1-fejl på Qwen ImageModellimiteringKør forespørgsler i en løkke i stedet
URL returnerer 403 senereURL udløbetDownload med det samme efter generering

Kilde: CometAPI billedgenereringsdokumentation

Fejlsøg fejl i videogenerering

Videogenerering fejler anderledes, fordi den er asynkron. Initialisér statusvariabler før løkken, så timeout-fejlmeddelelsen altid er veludformet:

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

Videospecifikke problemer:

SymptomÅrsagLøsning
Opgave sidder fast i queued 10+ minServerbelastningPrøv igen med en anden model
failed uden fejlbeskedPrompt filtreret eller model-fejlOmformuler prompt
Video-URL returnerer 403URL udløbetDownload med det samme
task_not_exist ved første poll på RunwayOpgave initialiseres stadig (CometAPI-dokumenteret adfærd)Vent 5s og prøv igen
Kling returnerer "succeed" ikke "succeeded"Klings API bruger en ikke-standard statusstrengHåndtér begge i polling-logik

Kilde: CometAPI videogenereringsdokumentation**, Kling Video-dokumentation

Node.js-version

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

En fejlsøgnings-tjekliste

Når en generering fejler, og du ikke ved, hvor du skal starte:

For tekstgenerering:

  • Er API-nøglen sat og i formatet Authorization: Bearer <key>?
  • Er finish_reason noget andet end stop?
  • Er content null? Tjek om finish_reason er tool_calls
  • Blev outputtet trunkeret? Tjek finish_reason: "length" og usage.completion_tokens
  • Er fejlen en 4xx (ret forespørgslen) eller 5xx (prøv igen)?
  • Består outputtet din validering på applikationslaget? (stiltiende fejl)

For billedgenerering:

  • Er data-arrayet tomt? (indholdsfilter)
  • Brugte du response_format på GPT Image 2? (ikke understøttet — brug output_format)
  • Satte du n > 1 på Qwen Image? (ikke understøttet)
  • Downloadede du billedet, før URL’en udløb?

For videogenerering:

  • Sidder opgaven fast i queued? (prøv en anden model)
  • Tjekkede du feltet error i svaret for en mislykket opgave?
  • Downloadede du videoen, før URL’en udløb?
  • Håndterer du både "succeed" (Kling) og "succeeded" (Veo, Runway)?

FAQ

Sp.: Min forespørgsel returnerer 200, men der er intet indhold. Hvad skete der?

Tjek finish_reason. Hvis det er content_filter, blev genereringen blokeret — forespørgslen lykkedes, men outputtet blev undertrykt. Hvis det er tool_calls, kaldte modellen et værktøj i stedet for at returnere tekst, og content er null med vilje. Hvis finish_reason er stop, men indholdet stadig er tomt, er det en stiltiende fejl — log det fulde svar og tjek din prompt.

Sp.: Hvordan ved jeg, om min prompt bliver filtreret?

For tekst: tjek finish_reason === "content_filter". For billeder: tjek om data-arrayet er tomt. For video: tjek om opgaven når status failed kort efter indsendelse uden fejlbesked. I alle tilfælde: prøv at omformulere prompten til at være mere neutral.

Sp.: Hvornår skal jeg forsøge igen med en mislykket forespørgsel?

Prøv igen på 429 og 5xx med eksponentiel backoff. Forsøg ikke igen på 4xx — en dårlig forespørgsel retter ikke sig selv. Undtagelsen er 401, hvis du roterer API-nøgler.

Sp.: Hvad er eksponentiel backoff, og hvorfor er det vigtigt?

I stedet for at forsøge igen med det samme venter du gradvist længere: 1s, 2s, 4s. At tilføje tilfældig jitter (+ random.random()) forhindrer, at flere klienter forsøger igen samtidigt. Dette er standardpraksis for ethvert API med rate limits — ikke specifikt for CometAPI.

Sp.: Videoopgaven sidder fast i queued i 10 minutter. Er den fejlet?

Ikke nødvendigvis — køer kan vokse under belastning. Vent op til din max_wait-tærskel, hæv derefter en TimeoutError og prøv igen med en anden model. Log opgave-ID’et, så du kan tjekke status manuelt om nødvendigt.

Sp.: Hvordan fanger jeg stiltiende fejl?

Stiltiende fejl kræver validering på applikationslaget — API’et fortæller dig ikke, at outputtet er semantisk forkert. Tjek, at outputtet matcher det forventede format (gyldig JSON, forventet label, minimumslængde). Log det fulde output, når valideringen fejler. De mest almindelige årsager er tvetydige prompts, ignorerede formatinstruktioner eller input, der er for kort eller for langt til opgaven.

Klar til at skære AI-udviklingsomkostninger med 20%?

Kom gratis i gang på få minutter. Gratis prøvekreditter inkluderet. Intet kreditkort påkrævet.

Læs mere