# Nano Banana 2: что делать при ошибке 503 «Model is Overloaded»

> При 503 в Nano Banana 2 повторяйте запрос с задержкой и ограничением времени. Если бюджет исчерпан, отложите задачу; успех подтверждайте полученным изображением, а не кодом 200.

- URL: https://blog.laozhang.ai/ru/posts/nano-banana-2-503-overloaded-fix
- Published: 2026-03-09
- Updated: 2026-10-07
- Author: LaoZhang AI Team (https://blog.laozhang.ai/ru/about)
- Topic: Генерация изображений
- Tags: Nano Banana 2, Ошибка 503, Gemini API, Повторные попытки

---
Если Nano Banana 2 отвечает **503 «Model is Overloaded»**, сначала сохраните точный HTTP-статус, модель и адрес сервиса. Для подтверждённого 503 используйте ограниченные повторные попытки со случайной задержкой. Ограничивайте и число обращений, и общее время ожидания: когда один из бюджетов закончился, приостановите задачу или передайте её в очередь. Успешная генерация — это полученное и декодируемое изображение; ответ 200 с одним текстом ещё не означает, что задача выполнена.

Ниже — пример для генерации и редактирования изображений через нативный Gemini API. Текущий идентификатор Nano Banana 2 — `gemini-3.1-flash-image`, согласно [карточке модели Google](https://ai.google.dev/gemini-api/docs/models/gemini-3.1-flash-image). Повторы позволяют переждать временную недоступность, но не обещают восстановить сервер или завершить каждую задачу.

## Проверьте статус и маршрут до первого повтора

503 означает, что сервис временно не может обработать запрос. Среди причин стандарт HTTP называет перегрузку и обслуживание; сам код не устанавливает ни конкретную причину, ни глобальный масштаб сбоя. Заголовок `Retry-After`, если он есть, указывает, когда можно повторить обращение. [Определение HTTP 503](https://www.rfc-editor.org/rfc/rfc9110.html#section-15.6.4).

В журнале оставьте время с часовым поясом, endpoint, API version, точный model ID, HTTP-статус, безопасный текст ошибки, request ID и длительность обращения. Удалите ключ, личное изображение и чувствительный prompt. Если 503 вернул сторонний gateway, он мог сформировать ошибку сам: из этого ответа нельзя автоматически заключить, что недоступен прямой Gemini API.

Для Google AI Studio и Gemini API проверьте [официальную страницу статуса](https://aistudio.google.com/status). Отсутствие опубликованного инцидента не доказывает, что сервис исправен для каждого запроса. Если нужно отделить аварию от сбоя конкретного приложения, воспользуйтесь [проверкой статуса Nano Banana 2 по поверхности и масштабу](https://blog.laozhang.ai/ru/posts/is-nano-banana-2-down).

![Действия по ответу Nano Banana 2: ограниченный повтор 503, уточнение лимита 429, исправление запроса, проверка изображения и сверка неизвестного исхода](https://blog.laozhang.ai/posts/ru/nano-banana-2-503-overloaded-fix/img/response-action-map.webp)

| Что действительно получено | Следующее действие |
|---|---|
| HTTP 503 или целочисленный `APIError.code == 503` | Повторить с задержкой в пределах двух бюджетов |
| 429 | Прочитать сообщение и проверить конкретный лимит. Кратковременное ограничение частоты допускает backoff; исчерпанная дневная квота требует ожидания сброса или отдельного решения |
| 400, 403, 404; 402 при исчерпанной предоплате | Исправить запрос, доступ, ресурс или состояние оплаты. Не повторять неизменный запрос циклом |
| 200, но нет изображения | Проверить `candidates`, `finish_reason`, сведения о блокировке и `parts`; отличить отказ генерации от ошибки разбора ответа |
| Разрыв соединения или клиентский timeout без ответа | Зафиксировать неизвестный исход. Не отправлять новый POST автоматически |

Google рекомендует backoff со случайной задержкой для временных ошибок, включая 503, и ограничение повторов в [руководстве по устранению неполадок](https://ai.google.dev/gemini-api/docs/troubleshooting). Пример ниже намеренно повторяет только подтверждённый 503: для других временных классов добавляйте отдельные правила, когда понимаете их причину.

У нативного Python SDK `APIError.code` — **целое число**, а `status` и `message` — дополнительные поля, согласно [определению APIError](https://github.com/googleapis/python-genai/blob/main/google/genai/errors.py). Не ищите подстроку `503` в любом исключении: она может встретиться в тексте, не обозначая HTTP-статус. Справочник с кодом `service_unavailable` относится к [Interactions API](https://ai.google.dev/gemini-api/docs/api-errors); это другой контракт ошибок, а не формат нативного `generateContent`.

## Генерация и редактирование с ограничением времени

Для нативного Gemini API используйте `google-genai` и `from google import genai`. В варианте REST запрос имеет вид `POST https://generativelanguage.googleapis.com/v1/models/gemini-3.1-flash-image:generateContent`. При генерации передаётся текст; при редактировании к нему добавляются байты исходного изображения с правильным MIME-типом. Выходные модальности `TEXT` и `IMAGE` разрешают модели вернуть изображение вместе с пояснением. Формат ввода и чтение частей ответа описаны в [руководстве Google по изображениям](https://ai.google.dev/gemini-api/docs/generate-content/image-generation).

В примере нужен уже настроенный доступ к Gemini API, пакет `google-genai` с используемыми ниже типами и Pillow для проверки изображения. Ключ передаётся аргументом из вашего действующего хранилища секретов. Функция не создаёт проекты, не меняет настройки аккаунта и не переключается на Vertex AI.

Параметры приложения здесь выбраны для иллюстрации: **4 обращения всего**, **90 секунд на ожидание API и задержки**, до **20 секунд на одно обращение**, случайная пауза в диапазоне от нуля до 2, затем 4 и 8 секунд. Это не рекомендации по времени восстановления Google. Даже максимальные 4 × 20 + 2 + 4 + 8 = 94 секунды урезаются общим бюджетом в 90 секунд; слишком длинный `Retry-After` приводит к отложенной задаче.

```python
import asyncio
import random
import time
from email.utils import parsedate_to_datetime
from io import BytesIO
from pathlib import Path

from google import genai
from google.genai import errors, types
from PIL import Image

def retry_after_seconds(value, wall_now=time.time):
    if not value:
        return None
    value = value.strip()
    if value.isdigit():
        return float(value)
    try:
        date = parsedate_to_datetime(value)
        if date.tzinfo is None:
            return None
        return max(0.0, date.timestamp() - wall_now())
    except (ValueError, TypeError, OverflowError):
        return None

def image_bytes(response):
    # Text and an empty candidate list are not image output.
    for candidate in response.candidates or []:
        content = candidate.content
        for part in (content.parts if content else []) or []:
            blob = part.inline_data
            if not blob or not blob.data:
                continue
            if not (blob.mime_type or "").startswith("image/"):
                continue
            raw = blob.data
            try:
                with Image.open(BytesIO(raw)) as image:
                    image.verify()
                with Image.open(BytesIO(raw)) as image:
                    image.load()
                    if image.width <= 0 or image.height <= 0:
                        continue
                    extension = {"PNG": ".png", "JPEG": ".jpg",
                                 "WEBP": ".webp"}.get(image.format)
                if extension:
                    return raw, extension
            except (OSError, ValueError, Image.DecompressionBombError):
                continue
    return None

async def bounded_image(send, *, attempts=4, budget=90.0,
                        now=time.monotonic, wait=asyncio.sleep,
                        rand=random.random):
    deadline = now() + budget
    for index in range(attempts):
        remaining = deadline - now()
        if remaining <= 0:
            return {"state": "deferred", "reason": "time_budget"}
        timeout_ms = max(1, int(min(20.0, remaining) * 1000))
        try:
            response = await asyncio.wait_for(
                send(timeout_ms), timeout=remaining)
        except errors.APIError as error:
            if error.code != 503:
                return {"state": "stopped", "reason": "api_error",
                        "code": error.code}
            headers = getattr(error.response, "headers", {}) or {}
            hint = retry_after_seconds(headers.get("Retry-After"))
            if index + 1 == attempts:
                return {"state": "deferred", "reason": "attempt_limit",
                        "retry_after_seconds": hint}
            pause = rand() * min(8.0, 2.0 * 2 ** index)
            if hint is not None:
                pause = max(pause, hint)
            if pause >= deadline - now():
                return {"state": "deferred", "reason": "retry_after_or_budget",
                        "retry_after_seconds": hint}
            await wait(pause)
            continue
        except Exception as error:
            # Includes timeout/disconnect: the POST may have been applied.
            return {"state": "unknown", "reason": type(error).__name__}
        result = image_bytes(response)
        if result is None:
            return {"state": "stopped", "reason": "no_valid_image",
                    "finish_reasons": [str(getattr(c, "finish_reason", None))
                                       for c in response.candidates or []],
                    "block_reason": str(getattr(
                        getattr(response, "prompt_feedback", None),
                        "block_reason", None))}
        raw, extension = result
        return {"state": "completed", "bytes": raw, "extension": extension}
    return {"state": "deferred", "reason": "attempt_limit"}

async def generate_image(api_key, prompt, *, input_path=None,
                         output_stem="result"):
    parts = [types.Part.from_text(text=prompt)]
    if input_path is not None:
        raw = Path(input_path).read_bytes()
        with Image.open(BytesIO(raw)) as image:
            mime = {"PNG": "image/png", "JPEG": "image/jpeg",
                    "WEBP": "image/webp"}.get(image.format)
            image.load()
        if mime is None:
            raise ValueError("Use a PNG, JPEG or WebP input image")
        parts.append(types.Part.from_bytes(data=raw, mime_type=mime))
    async with genai.Client(
        api_key=api_key,
        http_options=types.HttpOptions(
            api_version="v1",
            retry_options=types.HttpRetryOptions(attempts=1)
        )
    ).aio as client:
        async def send(timeout_ms):
            return await client.models.generate_content(
                model="gemini-3.1-flash-image",
                contents=parts,
                config=types.GenerateContentConfig(
                    response_modalities=["TEXT", "IMAGE"],
                    http_options=types.HttpOptions(timeout=timeout_ms)
                )
            )
        result = await bounded_image(send)
    if result["state"] == "completed":
        path = Path(output_stem + result.pop("extension"))
        path.write_bytes(result.pop("bytes"))
        result["path"] = str(path)
    return result
```

Для нового изображения вызывайте `await generate_image(api_key, "Создай изображение красного круга на белом фоне")`. Для редактирования добавьте `input_path="source.png"` и опишите изменение, например «Замени белый фон на светло-серый, сохрани форму круга». Эти вызовы должны выполняться внутри вашего асинхронного приложения; для отдельного скрипта используйте `asyncio.run(...)`.

`completed` возвращается после проверки байтов и сохранения файла. Эта проверка подтверждает, что файл можно открыть как изображение, но не оценивает качество выполнения prompt. Ошибка записи файла должна обрабатываться как локальная ошибка хранения — повторять генерацию из-за неё не нужно. При `no_valid_image` сохраните диагностические поля ответа без личных данных и проверьте причину: безопасность, завершение кандидата и неправильный разбор — разные ветки.

Проверка этого примера ограничена синтаксисом и локальными сценариями с искусственными ответами, часами и транспортом. Реальная генерация, задержки сервера и частота успешного восстановления здесь не измерялись.

### Почему автоматические повторы SDK отключены

Повтором в примере управляет `bounded_image`. Поэтому у SDK задано `HttpRetryOptions(attempts=1)`: число `attempts` включает исходный запрос. В `HttpOptions.timeout` значение задаётся **в миллисекундах**, не в секундах. Это следует из [типов официального Python SDK](https://github.com/googleapis/python-genai/blob/main/google/genai/types.py).

Документация troubleshooting описывает четыре автоматических повтора Python SDK, но в проверенной 6 октября реализации ветки `main` значение `retry_options=None` приводит к одной попытке. Эти сведения не устанавливают поведение каждой опубликованной версии пакета. Явная настройка убирает зависимость примера от такого расхождения; проверьте поддержку типов вашей версией. [Реализация повторов SDK](https://github.com/googleapis/python-genai/blob/main/google/genai/_api_client.py).

Назначьте одного владельца повторов и проверьте gateway. Четыре попытки приложения при пяти попытках SDK могут дать до двадцати обращений ещё до повторов посредника. Если повторяет SDK, уберите внешний цикл; если приложение — отключите внутренние повторы и ограничьте весь срок ожидания.

`Retry-After` допускает число секунд или HTTP-дату. Код выбирает не меньше указанной задержки. Если она не помещается в оставшееся время, запрос откладывается, а не отправляется раньше подсказки сервера. [Формат Retry-After](https://www.rfc-editor.org/rfc/rfc9110.html#section-10.2.3).

## Когда остановиться, поставить в очередь или включить резерв

`deferred` означает, что подтверждённые ошибки исчерпали локальную политику ожидания. Сохраните задачу с собственным ID, неизменными параметрами, числом попыток и временем следующего допуска. Если результат содержит `retry_after_seconds`, очередь должна выдержать эту задержку перед новым обращением. Покажите пользователю «Задача отложена» с возможностью отмены. Постановка в очередь не равна готовому изображению; состояние `completed` присваивается после проверки и сохранения результата.

`unknown` требует другого действия. При timeout или разрыве соединения сервер мог уже обработать POST, а ответ потерялся. Сохраните доступный request ID и проверьте журналы своего сервиса или посредника. Не запускайте автоматически ни повтор, ни резервную модель до разрешения неизвестного исхода. Локальный ID предотвращает двойное выполнение вашей очередью, но сам по себе не даёт серверной дедупликации. Ограничения автоматических повторов неидемпотентных запросов описаны в [RFC 9110](https://www.rfc-editor.org/rfc/rfc9110.html#section-9.2.2).

Для очереди задайте срок жизни задачи, предел попыток и атомарный захват одним worker. Повторный клик пользователя с тем же ID должен возвращать существующее состояние, а не создавать ещё одну генерацию. Исчерпанный бюджет одного интерактивного обращения можно заменить отдельным бюджетом фоновой задачи, но он тоже должен быть конечным.

| Вариант | Когда он подходит | Что проверить перед выбором |
|---|---|---|
| Пауза с ручным повтором | Пользователь может вернуться позже; задача не должна выполняться без него | Сохранённые параметры, понятная причина и отсутствие автоматического дубля |
| Ваша очередь | Нужно продолжить ту же задачу после разрешённой задержки | Один worker на задачу, срок жизни, предел попыток и отмена |
| Резервная модель | Результат нужен сейчас и изменение модели заранее допустимо | Генерация **и** нужное редактирование, формат ввода/вывода, приватность, разрешение, цена и общий бюджет |
| Gemini Batch API | Результат не нужен в текущем диалоге | Поддержка модели, отслеживание задания и проверка каждого ответа |

Текстовая модель `gemini-2.5-flash` не является резервом для получения изображения. Смена SDK, протокола или переход к Vertex AI также не подтверждают восстановление первоначального маршрута. Резерв включайте после определённого отказа, когда его возможности и расходы уже приняты; успешный ответ резерва проверяется тем же способом.

Nano Banana 2 поддерживает Batch. Google описывает его как асинхронную обработку с целевым сроком выполнения в пределах 24 часов, а не как гарантию немедленного результата или защиту от любых 503. Храните имя задания и отслеживайте его состояние вместо повторной отправки всего пакета. Возможны `FAILED`, `CANCELLED` и `EXPIRED`; даже у успешного задания нужно проверять ошибки отдельных запросов и наличие изображений. [Карточка модели](https://ai.google.dev/gemini-api/docs/models/gemini-3.1-flash-image), [документация Batch API](https://ai.google.dev/gemini-api/docs/batch-api).

## Ограничьте параллелизм и пропускайте только один пробный запрос

Повторы одного пользователя становятся нагрузкой, когда их одновременно запускают все worker. Ограничивайте число активных генераций на общий endpoint и модель, а задержанные задачи возвращайте в очередь. Начальное значение параллелизма выбирайте по собственным лимитам и измерениям; универсального числа потоков, которое устранит 503 у Nano Banana 2, нет. Задержка не должна удерживать рабочий слот, предназначенный для активного обращения.

![Три состояния circuit breaker при 503: обычная работа, пауза и атомарный допуск одной пробы, успешной только при полученном изображении](https://blog.laozhang.ai/posts/ru/nano-banana-2-503-overloaded-fix/img/circuit-breaker-states.webp)

Circuit breaker полезен, когда временные отказы повторяются у разных задач. При превышении вашего порога он прекращает новые обращения к этому маршруту на выбранный период. После паузы допускается пробный запрос; остальные задачи остаются в очереди. Успехом пробы считается полученное изображение, а не текст или один статус 200.

Главное условие полуоткрытого состояния — **атомарный допуск одной пробы**. Нельзя просто возвращать `True` каждому worker после окончания паузы. Под блокировкой либо атомарной операцией общего хранилища первый worker выставляет `probe_in_flight=True`; остальные не отправляют запрос. Разрешение освобождается при любом исходе. Для нескольких процессов нужен общий механизм допуска, а не отдельный флаг в памяти каждого процесса.

Пробу ограничьте одной попыткой и отдельным временем ожидания: внутренний цикл с четырьмя генерациями уже не является одной пробой. Неудача вновь открывает выключатель, успех разрешает постепенное возвращение нагрузки. При неизвестном сетевом исходе не запускайте немедленную заменяющую пробу. Пороги, длительность паузы и скорость возврата — параметры вашего приложения; они не означают, что Google восстановится через заданное число секунд.

## Частые вопросы об ошибке 503

### Можно ли считать все запросы с 503 бесплатными?

Нет подтверждения для такого общего обещания. [FAQ Google по биллингу](https://ai.google.dev/gemini-api/docs/billing) прямо говорит об отсутствии платы за токены при ошибках **400 и 500**, при этом они учитываются в квоте. Эта формулировка не гарантирует отсутствие расходов при каждом 503, потерянном ответе или обращении через посредника. Проверяйте фактическое использование у своего провайдера; новые попытки и резервная генерация могут создавать дополнительную работу.

### Поможет ли платный тариф убрать Model is Overloaded?

Переход на другой тариф не является гарантией устранения 503. Сначала различите временную недоступность и 429 с конкретным ограничением аккаунта. Для 429 решение зависит от того, исчерпана ли квота или превышена кратковременная частота; для подтверждённого 503 нужен ограниченный повтор либо отложенная задача. [Руководство Google по ошибкам](https://ai.google.dev/gemini-api/docs/troubleshooting).

### В какие часы лучше повторять генерацию и сколько ждать восстановления?

У этой статьи нет актуальных измерений, подтверждающих безопасные часы UTC или фиксированное время восстановления Nano Banana 2. Используйте `Retry-After`, если он есть, официальные обновления и свои журналы для того же маршрута. Срок ожидания пользователя задавайте бюджетом приложения; он не должен зависеть от обещания, что сервер обязательно восстановится через несколько минут.

### Нужно ли менять ключ при каждом 503?

Нет оснований менять ключ только из-за 503. Сохраните код, маршрут и request ID, затем примените ограниченную политику ожидания. Ошибки доступа и квоты проверяются отдельно; ротация ключей или проектов не подтверждает исправление серверной перегрузки и не заменяет диагностику.

## Официальная документация

Параметры запросов, цены и правила списания средств описаны в документации LaoZhang API для разработчиков: [биллинг и ошибки генерации изображений](https://docs.laozhang.ai/en/api-capabilities/image-billing-and-failures).

*Примечание: [laozhang.ai — наш собственный сервис](https://blog.laozhang.ai/ru/about#laozhang); его цены — наши тарифы, а не независимое сравнение.*

## Источники

Внешние страницы, на которые ссылается это руководство, в порядке упоминания. Последнее обновление: 2026-10-07.

- [карточке модели Google](https://ai.google.dev/gemini-api/docs/models/gemini-3.1-flash-image) (ai.google.dev)
- [Определение HTTP 503](https://www.rfc-editor.org/rfc/rfc9110.html) (rfc-editor.org)
- [официальную страницу статуса](https://aistudio.google.com/status) (aistudio.google.com)
- [руководстве по устранению неполадок](https://ai.google.dev/gemini-api/docs/troubleshooting) (ai.google.dev)
- [определению APIError](https://github.com/googleapis/python-genai/blob/main/google/genai/errors.py) (github.com)
- [Interactions API](https://ai.google.dev/gemini-api/docs/api-errors) (ai.google.dev)
- [руководстве Google по изображениям](https://ai.google.dev/gemini-api/docs/generate-content/image-generation) (ai.google.dev)
- [типов официального Python SDK](https://github.com/googleapis/python-genai/blob/main/google/genai/types.py) (github.com)
- [Реализация повторов SDK](https://github.com/googleapis/python-genai/blob/main/google/genai/_api_client.py) (github.com)
- [документация Batch API](https://ai.google.dev/gemini-api/docs/batch-api) (ai.google.dev)
- [FAQ Google по биллингу](https://ai.google.dev/gemini-api/docs/billing) (ai.google.dev)
