Skip to content

Ошибки кастомного провайдера в Codex CLI: stream disconnected, 401, 404 и wire_api ​

Вы настроили собственный model_provider в ~/.codex/config.toml, но Codex падает на первом же промпте либо начинает отвечать и обрывается на полуслове. Текст ошибки, который выводит Codex, позволяет быстро сузить круг причин, а каждое из описанных ниже решений сводится к правке одной-двух строк конфига.

Короткий ответ

Codex отправляет все запросы на base_url + /responses и работает только по протоколу Responses API. Поэтому base_url должен заканчиваться на корне версии (https://byesu.com/v1), а в wire_api должно быть указано "responses" — значение "chat" было удалено. Ошибка unexpected status 401 означает, что шлюз не получил подходящий ключ (проверьте requires_openai_auth и env_key). unexpected status 404 обычно указывает на неверный путь в URL. stream disconnected before completion говорит о том, что поток SSE завершился до события response.completed — чаще всего из-за прокси или таймаута, а не из-за вашей конфигурации.

Симптомы и их значения ​

Эти строки взяты напрямую из исходного кода Codex (protocol/src/error.rs, model-provider-info/src/lib.rs), поэтому вы можете сопоставить их со своим выводом один в один:

text
stream disconnected before completion: stream closed before response.completed
stream disconnected before completion: idle timeout waiting for SSE

Поток открылся, но закрылся без финального события response.completed, либо от него не поступало данных дольше таймаута простоя (idle timeout). Перед тем как показать это сообщение, Codex уже выполнил повторные попытки.

text
unexpected status 401 Unauthorized: <response body>, url: https://byesu.com/v1/responses
unexpected status 404 Not Found: <response body>, url: https://byesu.com/responses

Шлюз вернул неуспешный HTTP-статус. Тело ответа содержит текст от самого шлюза, а в суффиксе url: виден точный URL, к которому обратился Codex. Проверьте его в первую очередь.

text
`wire_api = "chat"` is no longer supported.
How to fix: set `wire_api = "responses"` in your provider config.
More info: https://github.com/openai/codex/discussions/7782

Конфигурация не загрузилась, поэтому запрос даже не отправлялся.

text
Missing environment variable: `BYESU_API_KEY`.

У провайдера задан параметр env_key = "BYESU_API_KEY", но эта переменная не установлена или пуста в процессе, из которого был запущен Codex.

Почему это происходит ​

  • Codex сам формирует URL запроса. Он обрезает слеши в конце base_url и добавляет responses. Так, https://byesu.com/v1 превращается в https://byesu.com/v1/responses, а https://byesu.com — в https://byesu.com/responses, которого не существует.
  • Остался только один сетевой протокол. В справочнике по конфигурации указано, что responses — «единственное поддерживаемое значение, используемое по умолчанию, если параметр опущен». Старые руководства, советующие ставить wire_api = "chat" для OpenAI-совместимых провайдеров, устарели.
  • Есть два способа передачи учетных данных, и они не комбинируются. При requires_openai_auth = true Codex использует стандартную аутентификацию OpenAI (ключ, сохраненный через codex login или в auth.json) и, согласно документации по аутентификации, игнорирует env_key. Без этого флага ключ берется из переменной окружения, указанной в env_key.
  • Область видимости таблиц в TOML. Любой ключ после заголовка [model_providers.x] принадлежит этой таблице до следующего заголовка. Поэтому строка model = ..., размещенная ниже такого заголовка, не попадает на верхний уровень конфигурации.

Решение 1: Укажите base_url до корня версии API ​

Что вы написали в base_urlКакой URL вызывает CodexРезультат
https://byesu.comhttps://byesu.com/responses404
https://byesu.com/v1/responseshttps://byesu.com/v1/responses/responses404
https://byesu.com/v1/chat/completions.../v1/chat/completions/responses404
https://byesu.com/v1 или https://byesu.com/v1/https://byesu.com/v1/responsesКорректно

Завершающий слеш ни на что не влияет — Codex его убирает. Протестируйте эндпоинт отдельно от Codex, чтобы понять, на чьей стороне проблема — в URL или в клиенте:

bash
curl -sS -w '\n%{http_code}\n' https://byesu.com/v1/responses \
  -H "Authorization: Bearer sk-your-token" \
  -H "Content-Type: application/json" \
  -d '{"model": "gpt-6.1-sol", "input": "Reply with: ok"}'

Если эта команда возвращает 200, а Codex по-прежнему получает 404, сравните поле url: из ошибки Codex с URL из команды выше. Еще одна причина ошибки 404: некоторые шлюзы возвращают 404 и на неизвестную модель, поэтому перед тем как менять URL, прочитайте тело ответа.

Решение 2: Установите wire_api = "responses" (или удалите эту строку) ​

toml
[model_providers.byesu]
name = "byesu"
base_url = "https://byesu.com/v1"
wire_api = "responses"   # единственное поддерживаемое значение (оно же по умолчанию)

Если провайдер предоставляет только /v1/chat/completions, актуальные версии Codex не смогут работать с ним напрямую. Переключение wire_api не поможет, так как других вариантов больше нет. Используйте эндпоинт с поддержкой Responses API (включая потоковые события), поскольку для завершения каждого шага Codex ждет событие response.completed.

Решение 3: Выберите один способ передачи ключа и убедитесь, что Codex его видит ​

Вариант А: requires_openai_auth = true (используется в нашем руководстве по настройке Codex). Codex отправляет ключ, сохраненный через собственный механизм входа. Сохраните ключ от шлюза либо официальной командой:

bash
printenv BYESU_API_KEY | codex login --with-api-key

либо напрямую в ~/.codex/auth.json:

json
{ "OPENAI_API_KEY": "sk-your-token" }

Поле в JSON всегда называется OPENAI_API_KEY, даже если ключ принадлежит стороннему шлюзу. В этом режиме env_key игнорируется, поэтому экспорт переменной окружения ничего не изменит. В зависимости от параметра cli_auth_credentials_store (file, keyring, auto, ephemeral) Codex также может хранить учетные данные в системном хранилище ключей ОС вместо auth.json. Если редактирование auth.json не дает эффекта, проверьте эту настройку или выполните codex login --with-api-key.

Вариант Б: env_key. Codex читает ключ из указанной переменной окружения и отправляет его как Bearer-токен:

toml
[model_providers.byesu]
name = "byesu"
base_url = "https://byesu.com/v1"
wire_api = "responses"
env_key = "BYESU_API_KEY"
bash
export BYESU_API_KEY="sk-your-token"   # добавьте в ~/.zshrc или ~/.bashrc для сохранения
codex

Ошибка Missing environment variable означает, что Codex не видит переменную. Она может быть не задана, состоять только из пробельных символов (что приравнивается к отсутствию) или экспортирована в другой сессии оболочки. В документации OpenAI также отмечается, что переменная, заданная в терминале, может быть недоступна приложению, запущенному из графического интерфейса.

Все еще получаете 401? Теперь, когда понятно, откуда берется ключ, проверьте сам токен. Убедитесь, что в него не попал лишний перенос строки, что ключ не удален и не отключен в панели управления шлюза, и что у вас не указаны одновременно requires_openai_auth = true и env_key, на который вы рассчитывали. Секцию [model_providers.x.auth] (получение токена через команду) нельзя совмещать с env_key, experimental_bearer_token или requires_openai_auth. Общие причины ошибки 401 разобраны в статье Ошибка аутентификации Claude API 401.

Решение 4: Убедитесь, что выбран именно ваш провайдер ​

Если в поле url: ошибки фигурирует api.openai.com или команда /status показывает другого провайдера, значит, блок вашего провайдера не задействован:

toml
# ~/.codex/config.toml: ключи верхнего уровня идут ПЕРВЫМИ
model = "gpt-6.1-sol"
model_provider = "byesu"

[model_providers.byesu]
name = "byesu"
base_url = "https://byesu.com/v1"
wire_api = "responses"
requires_openai_auth = true
  • Порядок имеет значение. Ключи model и model_provider должны находиться до первого заголовка [table], иначе парсер TOML отнесет их к этой таблице. В руководстве по подключению шлюзов от OpenAI на это обращают особое внимание.
  • Конфиг проекта не может менять провайдера. В локальном файле репозитория .codex/config.toml запрещено задавать model_provider, model_providers и openai_base_url. Codex проигнорирует их там и выведет предупреждение при запуске. Храните эти настройки в ~/.codex/config.toml.
  • Зарезервированные идентификаторы. Переопределять встроенные ID, такие как openai, нельзя. Чтобы перенаправить встроенного провайдера OpenAI на другой адрес, используйте ключ верхнего уровня openai_base_url вместо таблицы [model_providers.openai].
  • Проверка. Выполните codex doctor, запустите codex и введите /status, чтобы увидеть активную модель и провайдера.

Решение 5: Устранение stream disconnected before completion ​

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

toml
[model_providers.byesu]
# ...base_url, wire_api, auth как указано выше...
request_max_retries = 4          # повторы HTTP-запроса (по умолчанию 4)
stream_max_retries = 10          # переподключения при обрыве потока (по умолчанию 5)
stream_idle_timeout_ms = 600000  # допустимое время тишины в потоке (по умолчанию 300000)

Увеличение этих лимитов помогает пережить кратковременные сбои сети, но не исправит поток, который каждый раз обрывается по одной и той же причине. Обратите внимание на текст после двоеточия:

  • idle timeout waiting for SSE. За все время ожидания простоя не пришло ни одного пакета данных. Такое часто случается при высоком уровне рассуждений (reasoning effort), когда генерация первого видимого токена может занимать несколько минут, либо когда прокси буферизует поток. Увеличьте stream_idle_timeout_ms или снизьте model_reasoning_effort для текущей задачи.
  • stream closed before response.completed. Соединение закрылось раньше времени. Если это происходит на каждом запросе, значит, какой-то промежуточный узел обрывает или модифицирует поток: корпоративный прокси, узел nginx с включенной буферизацией или эндпоинт, отдающий чанки в формате Chat Completions вместо событий Responses API. Если ошибка возникает лишь изредка, дело обычно в нестабильности сети, и встроенный механизм повторов в большинстве случаев справляется с этим сам.

Настройки на стороне прокси (буферизация, таймауты простоя, CDN) подробно разобраны в статье Обрыв потокового соединения.

Если вы используете byesu ​

byesu — это шлюз AI API с OpenAI-совместимыми и нативными для Anthropic эндпоинтами: один ключ для доступа к GPT, Claude, Grok и Gemini с оплатой по мере использования (pay-as-you-go) без подписок. Особенности настройки для Codex:

  • base_url = "https://byesu.com/v1" и wire_api = "responses". byesu обслуживает Responses API по пути /v1/responses, поэтому URL, который формирует Codex, полностью совпадает с требуемым. Идентификаторы моделей, например gpt-6.1-sol или gpt-6-sol, указываются в параметре верхнего уровня model — их можно скопировать из списка моделей в панели управления.
  • Передача ключа по нашему руководству: укажите requires_openai_auth = true и сохраните свой токен sk- в поле OPENAI_API_KEY файла ~/.codex/auth.json (либо используйте Вариант Б выше, если предпочитаете переменную окружения).

Чтобы начать работу, создайте токен в панели управления и следуйте инструкции Быстрый старт. Полное пошаговое руководство со скриптом автоматической настройки доступно в статье Подключение Codex CLI.

FAQ ​

Что означает ошибка stream disconnected before completion в Codex CLI? ​

Потоковый ответ завершился до того, как Codex получил событие response.completed, либо данные не поступали в течение времени ожидания простоя (по умолчанию 300000 мс). Перед выводом ошибки Codex пытается переподключить оборванный поток до stream_max_retries раз (по умолчанию 5). Постоянные сбои обычно означают, что прокси буферизует или обрывает поток SSE, либо эндпоинт не полностью реализует Responses API.

Можно ли по-прежнему использовать wire_api = "chat" в Codex? ​

Нет. Текущие релизы Codex поддерживают только wire_api = "responses", и это же значение используется по умолчанию, если ключ не указан. Конфигурация с wire_api = "chat" не загрузится и выдаст сообщение о том, что chat больше не поддерживается. Ваш провайдер должен предоставлять OpenAI Responses API по адресу base_url/responses.

Каким должен быть base_url для кастомного провайдера в Codex? ​

Корневой URL API, включая путь версии — например, https://byesu.com/v1. Codex сам добавляет /responses, поэтому base_url без /v1 или уже заканчивающийся на /responses приводит к неверному пути и ошибке 404.

Почему Codex возвращает unexpected status 401 при работе с кастомным провайдером? ​

Шлюз не получил допустимый ключ. Проверьте, какой режим аутентификации использует провайдер: при requires_openai_auth = true Codex отправляет ключ, сохраненный через codex login или в auth.json, и игнорирует env_key; при использовании env_key он читает указанную переменную окружения, которая должна быть экспортирована в процессе, запускающем Codex.

Почему настройка model_provider игнорируется? ​

Есть две частые причины. Ключи верхнего уровня, такие как model и model_provider, размещенные после заголовка [model_providers.x], попадают в эту таблицу, а не на верхний уровень. Кроме того, локальный файл проекта .codex/config.toml не может задавать model_provider или model_providers — Codex игнорирует их там, выводя предупреждение при запуске. Поместите их в самое начало файла ~/.codex/config.toml.

Возникла проблема? Напишите в поддержку или в наш чат.