OpenAI previous_response_not_found: Причины и способы устранения
Вы пытаетесь продолжить диалог в Responses API с помощью previous_response_id и получаете HTTP 400: "Previous response with id 'resp_…' not found." Ниже описано, что нужно API для разрешения этого ID, пять ситуаций, когда это невозможно, и шаблон работы без сохранения состояния (stateless), который позволяет избежать этой ошибки.
Краткий ответ
Ошибка previous_response_not_found означает, что API не может загрузить ответ, на который вы ссылаетесь, и поэтому не может восстановить предыдущий контекст. Типичные причины: исходный вызов был выполнен с параметром store: false (или в рамках политики Zero Data Retention), ответ старше 30 дней, он был удален или ID получен от другого провайдера или аккаунта. Надежное решение — перестать зависеть от состояния на стороне сервера: храните диалог самостоятельно, отправляйте полный набор входных данных для каждого шага с store: false и передавайте состояние рассуждений через encrypted_content.
Симптомы и значение ошибки
Тело ответа, возвращаемое Python SDK от OpenAI:
{
"error": {
"message": "Previous response with id 'resp_abc123' not found.",
"type": "invalid_request_error",
"param": "previous_response_id",
"code": "previous_response_not_found"
}
}В руководстве по кодам ошибок OpenAI previous_response_not_found описывается как невозможность разрешения previous_response_id из доступного состояния. Это критическая ошибка: API не продолжит работу без этого контекста, а повторные запросы с тем же ID приводят к тому же результату.
Схожая ошибка возникает, если вы воспроизводите выходные элементы, содержащие серверные ID, при отключенном параметре store:
Item with id 'rs_…' not found. Items are not persisted when `store` is set to false. Try again with `store` set to true, or remove this item from your input.Причина та же — вы ссылаетесь по ID на то, что не было сохранено, — и решение также описано ниже.
Почему это происходит
- Исходный ответ был создан с
store: false. По умолчаниюstoreимеет значениеtrue, если оно не указано, но многие клиенты и фреймворки устанавливаютfalseиз соображений конфиденциальности. Ответ, который не был сохранен, нельзя использовать в дальнейшем. Это часто встречается в трекере проблем SDK агентов OpenAI, когда сессия выполнялась сstore=False, а последующий шаг пытался выстроить цепочку на её основе. - Zero Data Retention (ZDR). В руководстве OpenAI по управлению данными указано, что при включенном режиме ZDR параметр
storeвсегда обрабатывается какfalse, даже если в запросе установленоtrue, поэтому цепочки запросов в организациях с ZDR никогда не работают. - Истек срок хранения. Объекты Response по умолчанию хранятся 30 дней. Задача, которая возобновляет поток после этого периода, получит данную ошибку.
- Ответ был удален. Если ваш код (или задача по очистке) удаляет ответы, их ID становятся невалидными.
- ID был получен из другого источника. ID ответа имеет смысл только для той службы, которая его создала. ID от
api.openai.com, отправленный на шлюз; ID от одного шлюза, отправленный другому; или балансировщик нагрузки, который перенаправляет второй шаг диалога другому провайдеру, — всё это приведет к ошибке. В документации OpenAI не уточняется область видимости аккаунта для сохраненных ответов, поэтому считайте каждый ID приватным для того ключа и той конечной точки, которые его сгенерировали.
Также обратите внимание, что previous_response_id нельзя комбинировать с параметром conversation — используйте один механизм управления состоянием на запрос.
Решение 1: Отправляйте полный набор входных данных самостоятельно (режим Stateless)
Этот подход работает везде: вы владеете историей, и каждый запрос несет её в себе целиком.
from openai import OpenAI
client = OpenAI(api_key="sk-YOUR_TOKEN", base_url="https://byesu.com/v1")
history = [{"role": "user", "content": "Plan a migration from REST polling to webhooks."}]
resp = client.responses.create(
model="gpt-6.1-sol",
input=history,
store=False,
include=["reasoning.encrypted_content"], # безвредно там, где это уже значение по умолчанию
)
print(resp.output_text)
# Переносим выходные элементы модели (сообщения, рассуждения, вызовы инструментов) на следующий шаг
history += resp.output
history.append({"role": "user", "content": "Now list the risks."})
resp = client.responses.create(
model="gpt-6.1-sol",
input=history,
store=False,
include=["reasoning.encrypted_content"],
)
print(resp.output_text)Почему это работает:
- Нечего искать. Без
previous_response_idнет состояния на стороне сервера, которое могло бы потеряться. - Рассуждения (reasoning) сохраняются. В руководстве OpenAI по рассуждениям указано, что в режиме без сохранения состояния (
store: falseили ZDR) элементы рассуждений по умолчанию включают свойствоencrypted_content; передача этих элементов обратно позволяет модели продолжать рассуждения. API по-прежнему принимаетreasoning.encrypted_contentв параметреincludeдля совместимости. - Циклы инструментов (tool loops) остаются неизменными. Для вызова функций добавляйте элементы вызова функций модели и элементы
function_call_outputвhistoryточно так же, как вы делали бы это в любом цикле без сохранения состояния.
Если провайдер отклоняет воспроизведенные элементы с сообщением "Item with id … not found" (как указано выше), удалите поле id из этих элементов перед отправкой — такая ошибка сообщалась для некоторых прокси-серверов без сохранения состояния в трекере Codex от OpenAI.
Примечание о стоимости: это не дороже по токенам, чем использование цепочек. Руководство OpenAI по состоянию диалогов гласит, что при использовании previous_response_id все предыдущие входные токены в цепочке всё равно оплачиваются как входные. Длинные истории в любом случае увеличивают расход — см. превышение контекстного окна для получения информации об обрезке и сжатии, что также работает при store: false.
Решение 2: Автоматический переход к безсостоятельному режиму при сбое цепочки
Если вы хотите использовать previous_response_id там, где это работает (например, в OpenAI с включенным хранилищем), всё равно ведите локальную копию истории и переключайтесь на неё, если ID не удается разрешить:
import openai
def continue_turn(prev_id, history, new_user_msg, model="gpt-6-sol"):
history.append({"role": "user", "content": new_user_msg})
try:
resp = client.responses.create(
model=model,
previous_response_id=prev_id,
input=[{"role": "user", "content": new_user_msg}],
)
except openai.BadRequestError as e:
if getattr(e, "code", None) != "previous_response_not_found":
raise
# Цепочка разорвана: воспроизводим весь диалог без сохранения состояния
resp = client.responses.create(
model=model,
input=history,
store=False,
include=["reasoning.encrypted_content"],
)
history += resp.output
return respБез локальной копии не к чему возвращаться — предыдущие шаги были только на сервере, который только что сообщил вам, что их у него нет.
Решение 3: Не выстраивайте цепочки через разных провайдеров
Если ваш трафик может перемещаться между конечными точками — например, при отказоустойчивости между OpenAI и шлюзом, использовании нескольких шлюзов или маршрутизатора, который выбирает провайдера для каждого запроса, — previous_response_id от одного звена не разрешится на другом. Шлюзы могут вообще не сохранять ответы или направлять последующие запросы другому вышестоящему сервису, поэтому не полагайтесь на previous_response_id при работе с несколькими провайдерами. Используйте шаблон без сохранения состояния из Решения 1 для любой конфигурации с несколькими провайдерами.
Если вы работаете напрямую с OpenAI и хотите получить надежное серверное состояние, Conversations API предоставляет долгоживущий ID диалога; OpenAI отмечает, что элементы диалога не подпадают под 30-дневный срок хранения. Это всё равно привязано к конкретному провайдеру, поэтому ограничение по переносимости остается в силе.
Codex и другие клиентские агенты
Агенты для программирования сталкиваются с этой проблемой, когда предполагают наличие состояния на сервере. История OpenAI Codex — полезный пример:
- Оригинальный Codex CLI добавил опцию
disable_response_storageдля организаций с политикой ZDR, чтобы клиент сохранял элементы ответов самостоятельно. - В сентябре 2025 года в PR #3212 ("Never store requests") эта опция была удалена из Codex, а последующие изменения установили
store: falseдля каждого запроса Responses. Текущий Codex хранит историю локально и воспроизводит её с зашифрованными рассуждениями, поэтому он не зависит отprevious_response_idпо HTTP. Старые руководства и конфигурации, в которых всё ещё естьdisable_response_storage = true, относятся к тому раннему периоду; текущие релизы больше не читают эту опцию. - Исходный код Codex действительно использует
previous_response_idв своем транспорте WebSocket для Responses. Для пользовательских провайдеров параметрsupports_websocketsпо умолчанию равенfalse; оставьте его выключенным, если провайдер явно не поддерживает режим Responses WebSocket.
Руководство byesu по Codex CLI показывает рабочий конфиг провайдера для https://byesu.com/v1.
Если вы вызываете GPT через byesu
byesu — это API-шлюз для ИИ с OpenAI-совместимой конечной точкой https://byesu.com/v1, поддерживающий как Chat Completions, так и Responses, с оплатой по факту использования без подписки.
- Предпочитайте запросы без сохранения состояния. Отправляйте полный ввод с
store: false, как в Решении 1; это работает одинаково на любой OpenAI-совместимой конечной точке и полностью устраняет данный класс ошибок. - Никогда не используйте ID повторно между конечными точками. ID вида
resp_…, созданный у другого провайдера, здесь не разрешится, и наоборот — сохраняйте свою собственную историю для всего, что может переключаться между конечными точками.
Чтобы начать работу, следуйте Quick Start.
Часто задаваемые вопросы
Что означает ошибка previous_response_not_found?
Это ошибка 400 invalid_request_error с параметром previous_response_id. API Responses не смог найти переданный вами ID в доступном ему состоянии, поэтому он не может продолжить цепочку. Генерация не происходит; повторные попытки с тем же ID приводят к той же ошибке.
Почему мой ID ответа не найден сразу после его создания?
Чаще всего исходный запрос был выполнен с параметром store, установленным в false, поэтому ответ не был сохранен. Организации с политикой Zero Data Retention обрабатываются как store: false, даже если вы отправляете true. Другая распространенная причина — отправка ID другому провайдеру или на конечную точку, отличную от той, которая его создала.
Как долго хранятся ответы Responses API?
OpenAI по умолчанию хранит объекты Response в течение 30 дней, если параметр store установлен в true (это значение по умолчанию, если параметр опущен). По истечении этого срока ID больше нельзя использовать в previous_response_id. Элементы, добавленные в объект Conversation, не подпадают под 30-дневный срок хранения (TTL).
Как продолжить диалог без previous_response_id?
Храните историю диалога самостоятельно: добавляйте выходные элементы каждого ответа в свой список ввода и отправляйте полный список в каждом запросе с параметром store, установленным в false. Элементы рассуждений в этом случае содержат encrypted_content, поэтому состояние рассуждений сохраняется между итерациями без необходимости хранения чего-либо на стороне сервера.
Нужен ли Codex параметр disable_response_storage?
Эта опция была удалена из кодовой базы Codex в сентябре 2025 года (openai/codex PR #3212), и текущий Codex отправляет store: false в каждом запросе Responses. Codex хранит историю локально и повторно отправляет её, поэтому он не зависит от previous_response_id по протоколу HTTP.
