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

Como depurar gerações com falha da API de IA

CometAPI
AnnaJun 4, 2026
Como depurar gerações com falha da API de IA

Falhas em APIs de IA são diferentes de falhas em APIs comuns. Uma resposta 200 não significa que sua geração foi bem-sucedida. Um campo de conteúdo null nem sempre é um erro. E o mesmo prompt que funcionou ontem pode falhar hoje porque um provedor atualizou sua política de conteúdo.

Este guia explica como ler erros de APIs de IA, o que cada modo de falha realmente significa e como criar tratativas de erro que informem o que quebrou em vez de apenas que algo quebrou.

Nota: Nomes de modelos como gpt-5.4 e gpt-5.4-mini usados neste artigo são identificadores da plataforma da CometAPI. Eles funcionam somente via https://api.cometapi.com/v1 — não diretamente pelas APIs da OpenAI ou Anthropic. Veja a lista completa de modelos.

Por que depurar APIs de IA é mais difícil do que depurar APIs comuns

Em uma API REST típica, um 200 significa sucesso e um 4xx significa que você fez algo errado. APIs de IA adicionam uma terceira categoria: falhas suaves — respostas que retornam 200 mas não contêm conteúdo utilizável.

Três coisas podem dar errado:

  1. Falha grave — erro HTTP (4xx, 5xx). A requisição não foi concluída.
  2. Falha suave — HTTP 200, mas finish_reason é content_filter ou length, ou content é null.
  3. Falha silenciosa — HTTP 200, o conteúdo parece ok, mas a saída está errada de um modo que você só detecta na camada da aplicação.

A maior parte do tratamento de erros cobre apenas o caso 1. Os casos 2 e 3 são onde vivem a maioria dos bugs em produção.

Entenda o formato de resposta de erro

O endpoint de conclusões de texto retorna uma estrutura de erro consistente:

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

Os endpoints de imagem e vídeo retornam formatos de erro diferentes — sempre analise o corpo bruto da resposta em vez de assumir uma estrutura fixa entre endpoints.

O campo message geralmente diz exatamente o que está errado. O campo param informa qual parâmetro causou o problema. Sempre registre ambos.

Saiba o que cada código de status HTTP significa

StatusSignificadoCausa comumCorreção
400Requisição inválidaFalta modelo, parâmetro errado para este modeloVerifique error.param na resposta
401Não autorizadoChave de API errada ou ausenteVerifique o formato Authorization: Bearer
429Limitado por taxaMuitas requisiçõesBackoff exponencial (veja a Etapa 4)
500Erro do servidorProblema no provedor ou corpo da requisição malformadoTente novamente com backoff; verifique o formato da requisição
504Tempo limite do gatewayO provedor demorou demaisTente novamente; considere um modelo mais rápido

Fonte**: CometAPI chat completions docs

A distinção entre 400 e 500 é importante para a lógica de retry. Um 400 significa que sua requisição está errada — repetir a mesma requisição não ajudará. Um 500 ou 504 significa que o servidor teve um problema — faz sentido tentar novamente.

Verifique finish_reason — o campo mais negligenciado

Uma resposta 200 com finish_reason: "content_filter" significa que sua geração foi bloqueada. O campo content estará null ou vazio. Se você não verificar isso, seu app retorna silenciosamente nada.

finish_reasonSignificadoO que fazerCorreção
stopConclusão normalNada — isto é sucessoVerifique error.param na resposta
lengthAtingiu o limite de tokensAumente max_tokens ou encurte o promptVerifique o formato Authorization: Bearer
content_filterBloqueado pela política de segurançaReformule o prompt; evite nomes/tópicos específicosBackoff exponencial (veja a Etapa 4)
tool_callsO modelo chamou uma ferramenta em vez de retornar textoTrate a chamada de ferramenta; content será nullTente novamente com backoff; verifique o formato da requisição
504Tempo limite do gatewayO provedor demorou demaisTente novamente; considere um modelo mais rápido

Fonte**: CometAPI chat completions docs

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

Detecte falhas silenciosas na camada da aplicação

Falhas silenciosas são as mais difíceis de capturar. A API retorna 200, finish_reason é stop, mas a saída está semanticamente errada. Você só consegue capturá-las na camada da aplicação.

Padrões comuns:

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

Falhas silenciosas geralmente têm três origens: o prompt é ambíguo, o modelo ignorou suas instruções de formato, ou a entrada era curta/demorada demais para a tarefa. Registrar toda a saída quando a validação falhar é a maneira mais rápida de diagnosticar qual delas é.

Adicione backoff exponencial para limites de taxa

Erros de limite de taxa (429) são temporários. A resposta correta é esperar e tentar novamente com atrasos crescentes — uma prática padrão para qualquer API com limites de taxa:

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

Não tente novamente em 400 ou 401 — são erros do cliente que não se resolvem sozinhos.

Depure falhas de geração de imagens

A geração de imagens tem seus próprios modos de falha além dos erros HTTP padrão:

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    }

Problemas específicos de imagem a observar:

SintomaCausaCorreção
Array data vazioPrompt filtradoVerifique revised_prompt; reformule
erro de response_format no GPT Image 2Parâmetro não suportadoUse output_format em vez de
n > 1 erro no Qwen ImageLimitação do modeloFaça requisições em loop
URL retorna 403 mais tardeURL expiradaBaixe imediatamente após a geração

Fonte**: CometAPI image generation docs

Depure falhas de geração de vídeo

A geração de vídeo falha de maneira diferente porque é assíncrona. Inicialize variáveis de status antes do loop para que a mensagem de timeout seja sempre bem formada:

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

Problemas específicos de vídeo:

SintomaCausaCorreção
Tarefa presa em queued por 10+ minCarga do servidorTente novamente com outro modelo
failed sem detalhe de erroPrompt filtrado ou erro do modeloReformule o prompt
URL do vídeo retorna 403URL expiradaBaixe imediatamente
task_not_exist no primeiro poll do RunwayA tarefa ainda está inicializando (comportamento documentado da CometAPI)Aguarde 5s e tente novamente
Kling retorna "succeed" não "succeeded"A API do Kling usa string de status não padronizadaTrate ambas no polling

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

Versão em 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);}

Uma lista de verificação de depuração

Quando uma geração falhar e você não souber por onde começar:

Para geração de texto:

  • A chave de API está definida e no formato Authorization: Bearer <key>?
  • finish_reason é algo diferente de stop?
  • content está nulo? Verifique se finish_reason é tool_calls
  • A saída foi truncada? Verifique finish_reason: "length" e usage.completion_tokens
  • O erro é um 4xx (corrija a requisição) ou 5xx (tente novamente)?
  • A saída passa sua validação na camada da aplicação? (falha silenciosa)

Para geração de imagens:

  • O array data está vazio? (filtro de conteúdo)
  • Você usou response_format no GPT Image 2? (não é suportado — use output_format)
  • Você definiu n > 1 no Qwen Image? (não é suportado)
  • Você baixou a imagem antes que a URL expirasse?

Para geração de vídeo:

  • A tarefa está presa em queued? (tente um modelo diferente)
  • Você verificou o campo error na resposta da tarefa que falhou?
  • Você baixou o vídeo antes que a URL expirasse?
  • Você está tratando tanto "succeed" (Kling) quanto "succeeded" (Veo, Runway)?

FAQ

P: Minha requisição retorna 200 mas não há conteúdo. O que aconteceu?

Verifique finish_reason. Se for content_filter, a geração foi bloqueada — a requisição foi bem-sucedida, mas a saída foi suprimida. Se for tool_calls, o modelo chamou uma ferramenta em vez de retornar texto, e content é nulo por design. Se finish_reason for stop mas o conteúdo ainda estiver vazio, isso é uma falha silenciosa — registre a resposta completa e verifique seu prompt.

P: Como sei se meu prompt está sendo filtrado?

Para texto: verifique se finish_reason === "content_filter". Para imagens: verifique se o array data está vazio. Para vídeo: verifique se a tarefa chega a failed pouco após o envio, sem detalhes de erro. Em todos os casos, tente reformular o prompt para ser mais neutro.

P: Quando devo tentar novamente uma requisição que falhou?

Tente novamente em 429 e 5xx usando backoff exponencial. Não tente novamente em 4xx — uma requisição inválida não se corrige sozinha. A exceção é 401 se você estiver rotacionando chaves de API.

P: O que é backoff exponencial e por que isso importa?

Em vez de tentar novamente imediatamente, você espera progressivamente mais: 1s, 2s, 4s. Adicionar jitter aleatório (+ random.random()) evita que vários clientes tentem novamente em sincronia. Isso é prática padrão para qualquer API com limites de taxa — não é específico da CometAPI.

P: A tarefa de vídeo está presa em queued por 10 minutos. Falhou?

Não necessariamente — as filas podem se acumular sob carga. Aguarde até seu limite max_wait, então lance um TimeoutError e tente novamente com um modelo diferente. Registre o ID da tarefa para poder verificar o status manualmente, se necessário.

P: Como capturar falhas silenciosas?

Falhas silenciosas requerem validação na camada da aplicação — a API não vai informar que a saída está semanticamente errada. Verifique se a saída corresponde ao formato esperado (JSON válido, rótulo esperado, comprimento mínimo). Registre toda a saída quando a validação falhar. As causas mais comuns são prompts ambíguos, instruções de formato ignoradas ou entradas curtas ou longas demais para a tarefa.

Pronto para reduzir os custos de desenvolvimento de IA em 20%?

Comece gratuitamente em minutos. Créditos de avaliação gratuita incluídos. Não é necessário cartão de crédito.

Leia Mais