# Diagnóstico de Nano Banana API: 400, 429, 503, NO_IMAGE y timeout

> Separa error HTTP de petición, motivo de finalización y timeout del llamador antes de corregir, revisar límites o aplicar retry limitado; incluye código y escalado.

- Source: https://www.aifreeapi.com/es/posts/nano-banana2-api-error-codes
- Language: es
- Published: 2026-08-05
- Updated: 2026-08-05
- Publisher: AI Free API (https://www.aifreeapi.com)

Cuando falla una solicitud de imagen de Nano Banana, conserva la evidencia cruda antes de reintentar. `400/429/503` son resultados HTTP de la petición, `NO_IMAGE` es un motivo de finalización oficial en la referencia actual de Google, y un timeout puede significar solo que el cliente o gateway dejó de esperar. No deben entrar en el mismo bucle de retry.

| Síntoma | Evidencia que debes guardar | Primera acción | ¿Repetir igual? | Prueba de recuperación |
|---|---|---|---|---|
| `400 INVALID_ARGUMENT` | URL final, API version, modelo, body anonimizado, details | Corregir el contrato de petición | No | La petición mínima deja de devolver 400 |
| `429 RESOURCE_EXHAUSTED` | Status/details, límites actuales, hora, intento | Identificar el límite y suavizar tráfico | Solo con límite | Éxito estable con concurrencia controlada |
| `503 UNAVAILABLE` | Request ID, ruta, modelo, intervalo | Consultar estado y aplicar backoff | Solo con límite | El probe mínimo funciona en la misma ruta |
| `finishReason=NO_IMAGE` | Candidate, `finishMessage`, parts, response ID | Revisar modalidad y ejecutar probe mínimo | Tras clasificar | Nueva respuesta con parte de imagen decodificable |
| timeout o `504` | Deadlines por capa, duración, presencia de body, request ID | Hallar la primera capa y comprobar resultado incierto | Solo si es transitorio | Finaliza por la misma ruta sin duplicado |

La [guía oficial de Gemini API](https://ai.google.dev/gemini-api/docs/troubleshooting) recomienda backoff exponencial limitado para errores transitorios como 408, 429 y 5xx, y no reintentar 400/403 a ciegas. [API errors](https://ai.google.dev/gemini-api/docs/api-errors) incluye `no_image` como error de generación; [GenerateContent](https://ai.google.dev/api/generate-content) define `NO_IMAGE` como `FinishReason`: se esperaba una imagen, pero no se generó. Es un resultado oficial, no un estado HTTP, y por sí solo no demuestra safety, sobrecarga ni facturación.

## Conserva la respuesta antes de que el adaptador la simplifique

Registra estos campos antes de convertir el error en un mensaje para el usuario. Anonimiza prompt, imágenes de entrada y credenciales.

```text
timestamp_utc, provider, base_url, api_version, endpoint, model
http_status, canonical_status, request_id, upstream_request_id
attempt, started_at, latency_ms, client_timeout_ms
candidate_count, prompt_block_reason, finish_reason
part_mime_types, has_text_part, has_image_part, error_body_summary
```

Nano Banana es un nombre común, no un identificador de protocolo. Guarda la ruta real. Gemini Developer API, Vertex AI, un gateway externo y tu reverse proxy pueden mostrar el mismo síntoma con responsabilidades diferentes. El probe siguiente usa la forma nativa de Gemini Developer API `models/{model}:generateContent`; no la mezcles con rutas Vertex de project/location.

```bash
API_VERSION="${GEMINI_API_VERSION:-v1beta}"
MODEL="${GEMINI_MODEL:?set GEMINI_MODEL}"

curl --fail-with-body --silent --show-error \
  -X POST \
  "https://generativelanguage.googleapis.com/${API_VERSION}/models/${MODEL}:generateContent" \
  -H "x-goog-api-key: ${GEMINI_API_KEY:?set GEMINI_API_KEY}" \
  -H "Content-Type: application/json" \
  -d '{
    "contents": [{"parts": [{"text": "Genera una foto de producto de una taza de cerámica azul sobre fondo blanco."}]}],
    "generationConfig": {"responseModalities": ["IMAGE"]}
  }'
```

Configura `GEMINI_MODEL` con un modelo de imagen que la documentación oficial actual muestre como disponible para tu cuenta. Si el probe funciona y la petición original no, el problema se reduce a entrada, contexto o parámetros opcionales. Si ambos fallan, revisa cuenta, ruta y capacidad.

![Capas de error HTTP, motivo de finalización y timeout del llamador en Nano Banana API](https://www.aifreeapi.com/posts/es/nano-banana2-api-error-codes/img/response-layers.webp)

## Un 400 se arregla comparando la petición

`400 INVALID_ARGUMENT` significa que la API ha rechazado la estructura o los parámetros. Repetir el mismo body no ayuda. Compara URL final, API version, modelo, `responseModalities` y MIME de entrada con la [documentación actual de generación de imágenes](https://ai.google.dev/gemini-api/docs/generate-content/image-generation).

Elimina campos opcionales hasta dejar una parte de texto y salida `IMAGE`. Después añade un campo por prueba. Un mínimo que funciona y falla al incorporar un parámetro es una evidencia mucho mejor que un payload grande que “parece correcto”. Si envías una imagen, registra el MIME real, método de transferencia y tamaño en bytes, no solo la extensión del fichero.

## 429 y 503 comparten backoff, no causa

`429 RESOURCE_EXHAUSTED` indica que has superado uno de los límites aplicables. Puede ser RPM, TPM, RPD, gasto u otro límite de modelo/nivel. Los valores cambian: compruébalos en [Google AI Studio rate limits](https://ai.google.dev/gemini-api/docs/rate-limits) y no fijes en código un número de una guía antigua.

Reduce picos antes de pedir más cuota. Limita worker concurrency, usa una cola y reparte solicitudes. Si todos los workers despiertan tras la misma pausa, generan otra tormenta de reintentos.

`503 UNAVAILABLE` es indisponibilidad temporal o sobrecarga. No demuestra que el prompt sea incorrecto ni promete recuperación en un plazo fijo. Compara un probe mínimo en la misma cuenta/modelo/ruta, consulta el estado oficial y guarda request ID. Detén el automatismo al agotar el presupuesto de intentos o tiempo.

```python
import random
import time
import requests

TRANSIENT = {408, 429, 500, 502, 503, 504}

def post_with_retry(
    url, headers, payload, *, max_attempts=4, total_budget_s=120
):
    deadline = time.monotonic() + total_budget_s

    for attempt in range(1, max_attempts + 1):
        remaining = deadline - time.monotonic()
        if remaining <= 0:
            raise TimeoutError("presupuesto total de retry agotado")

        try:
            response = requests.post(
                url, headers=headers, json=payload,
                timeout=(10, min(60, remaining))
            )
        except (requests.Timeout, requests.ConnectionError):
            if attempt == max_attempts:
                raise
        else:
            if response.status_code == 400:
                raise ValueError(f"corrige la petición: {response.text[:500]}")
            if response.status_code not in TRANSIENT:
                response.raise_for_status()
                return response.json()
            if attempt == max_attempts:
                response.raise_for_status()

        base = min(2 ** (attempt - 1), 30)
        delay = base + random.uniform(0, base * 0.5)
        if delay >= deadline - time.monotonic():
            raise TimeoutError("el siguiente retry excede el presupuesto")
        time.sleep(delay)
```

Usa una sola capa de retry. Si el SDK ya reintenta, otro bucle exterior puede multiplicar llamadas. Los valores del ejemplo son parámetros de aplicación ajustables, no una promesa del proveedor.

## `NO_IMAGE` es un resultado de generación, no un error HTTP

No confundas el `finishReason` oficial con un mensaje genérico de la aplicación. En el contrato actual de Google, `NO_IMAGE` pertenece al candidate; un gateway puede mapearlo aparte a `no_image`. Conserva el body upstream y recórrelo así:

1. Sin `candidates`: mira `promptFeedback`; un problema del prompt puede detener la respuesta antes de crear candidatos.
2. `finishReason=NO_IMAGE`: guarda `finishMessage`, `responseId` y `modelVersion`; revisa la modalidad y ejecuta el probe mínimo por la misma ruta. No lo renombres como safety o 503 sin evidencia.
3. `IMAGE_SAFETY`, `IMAGE_PROHIBITED_CONTENT` u otro motivo de policy: revisa una petición legítima o acepta el límite; retry no es una evasión.
4. Solo partes de texto: lee el texto y verifica la modalidad. Una salida solo-texto no es la misma evidencia que `NO_IMAGE`.
5. Hay `inlineData` pero la app dice que no hay imagen: revisa parser, MIME, base64 y escritura en almacenamiento.

```python
def inspect_result(body):
    candidates = body.get("candidates") or []
    if not candidates:
        return {"kind": "no_candidates", "feedback": body.get("promptFeedback")}

    candidate = candidates[0]
    finish_reason = candidate.get("finishReason")
    parts = ((candidate.get("content") or {}).get("parts") or [])
    images = [p["inlineData"] for p in parts if p.get("inlineData")]
    texts = [p["text"] for p in parts if p.get("text")]

    if images:
        kind = "image"
    elif finish_reason == "NO_IMAGE":
        kind = "no_image"
    elif finish_reason in {
        "SAFETY", "IMAGE_SAFETY", "PROHIBITED_CONTENT",
        "IMAGE_PROHIBITED_CONTENT", "IMAGE_RECITATION",
    }:
        kind = "blocked_or_policy"
    else:
        kind = "candidate_without_image"

    return {
        "kind": kind,
        "finishReason": finish_reason,
        "finishMessage": candidate.get("finishMessage"),
        "imageCount": len(images),
        "textCount": len(texts),
        "responseId": body.get("responseId"),
        "modelVersion": body.get("modelVersion"),
    }
```

Para profundizar en seguridad y respuestas de solo texto, usa la [guía de Gemini sin imagen e IMAGE_SAFETY](/es/topics/nano-banana). Si hay también errores de autenticación u otros HTTP, consulta la [guía general de errores Gemini API](/es/posts/gemini-api-error-troubleshooting-guide).

## Timeout: localiza la primera capa que cerró

```text
client/SDK → application → reverse proxy/API gateway → provider → model
```

Si el cliente cancela primero, quizá no haya body HTTP de Google, y un reverse proxy puede crear su propio 504. Registra un 504 del proveedor solo cuando aparece en la respuesta upstream. Lo contrario también importa: el timeout del cliente no demuestra que el servidor se detuvo. Antes de repetir una operación que crea una imagen o escritura downstream, consulta task o artifact por request/response ID para evitar duplicados. Mide inicio, primer byte, fin, deadline y actor que cerró cada tramo.

Compara por la misma cuenta/ruta/modelo el probe mínimo y la entrada original. Si el primero funciona y la petición compleja termina siempre en el deadline del cliente, ajusta la capa correcta o reduce la entrada. Si ambos fallan en la misma ventana 503/504, repetir la carga pesada aporta poca información.

![Línea temporal de retry limitado, parada y escalado para Nano Banana API](https://www.aifreeapi.com/posts/es/nano-banana2-api-error-codes/img/retry-timeline.webp)

## Cuándo parar y qué enviar a soporte

Detén los reintentos ante 400 u otro client error conocido; si `NO_IMAGE` aún no está clasificado con probe mínimo y respuesta cruda; ante un `finishReason` de safety/policy; al agotar intentos/tiempo total; ante 5xx persistente para el mismo fingerprint; o cuando el llamador ya no pueda usar el resultado.

Entrega a soporte: intervalo UTC, provider/base URL, API version, endpoint/modelo, body de error anonimizado, request/upstream request ID, latencia de cada intento, timeout por capa, prompt mínimo, MIME de entrada, número de candidates, `promptFeedback`, `finishReason`, `finishMessage`, `responseId`, `modelVersion`, MIME de las partes y resultado de buscar artifacts tras el timeout.

La reparación solo está demostrada cuando la petición mínima devuelve una parte de imagen decodificable por la ruta real y la carga original termina después con concurrencia controlada. HTTP 200, task `completed` o la desaparición de `NO_IMAGE` en el adaptador no bastan por sí solos.
