Summary
When the openai-codex provider (api openai-codex-responses, baseUrl
https://chatgpt.com/backend-api/codex) is invoked with a context whose
messages array is empty but whose systemPrompt is set, the resulting
request body contains input: []. ChatGPT's codex backend rejects this with
a 400:
One of "input" or "previous_response_id" or 'prompt' or 'conversation_id'
must be provided.
In practice the 400 closes the upstream socket before the streaming consumer
classifies the failure, so most calls surface to the user as a 15–31s
timeout rather than the underlying error — making this hard to diagnose
from logs alone. We caught the rawError on one occurrence:
[agent/embedded] embedded run agent end: ... isError=true model=gpt-5.5
provider=openai-codex
error=One of "input" or "previous_response_id" or 'prompt' or 'conversation_id' must be provided.
rawError=One of "input" or "previous_response_id" or 'prompt' or 'conversation_id' must be provided.
with 27 sibling entries from the same workload showing
decision=surface_error reason=timeout from=openai-codex/gpt-5.5 for what
appears to be the same root cause.
Environment
- openclaw
2026.4.26
- macOS 15 (Darwin 25.4.0)
- Auth: ChatGPT OAuth (access_token still valid; not an auth failure)
- Triggering plugin:
active-memory (memory-flush bootstrap run)
- Affected models:
openai-codex/gpt-5.4, openai-codex/gpt-5.5
Root cause (suspected)
buildOpenAIResponsesParams in provider-stream-*.js:
```js
const messages = convertResponsesMessages(model, context, ..., {
includeSystemPrompt: !isCodexResponses, // codex skips system msg
supportsDeveloperRole
});
const params = {
model: model.id,
input: messages, // empty when caller has no user turn
stream: true,
...isCodexResponses ? { instructions: buildOpenAICodexResponsesInstructions(context) } : {},
...
};
```
For codex, the system prompt is moved out of messages into instructions
(correct per ChatGPT's codex contract). But if the caller passes
{ systemPrompt: "...", messages: [] } — which active-memory's flush
bootstrap does — params.input ends up as []. The codex backend
treats that as missing and 400s.
Proposed fix
Inside buildOpenAIResponsesParams, after the convertResponsesMessages
call, ensure codex requests carry at least one input item when the only
content available is the system prompt:
```js
if (isCodexResponses && messages.length === 0 && context.systemPrompt) {
messages.push({
role: "user",
content: [{
type: "input_text",
text: sanitizeTransportPayloadText(
stripSystemPromptCacheBoundary(context.systemPrompt)
)
}]
});
}
```
Alternatives considered:
- Set
includeSystemPrompt: true for codex unconditionally — wrong, the
ChatGPT codex backend expects system-as-instructions, not as a message.
- Refuse to dispatch when
messages.length === 0 — surfaces a clearer
error but doesn't restore functionality for the (legitimate) bootstrap case.
Repro
- Configure an
openai-codex provider with gpt-5.5 (or any
openai-codex-responses model).
- Enable
active-memory plugin, set
plugins.entries.active-memory.config.activeProvider = openai-codex,
activeModel = gpt-5.5.
- Trigger an active-memory flush (e.g. send a Slack message that
crosses the flush threshold).
- Observe `[agent/embedded] embedded run failover decision: ... reason=timeout from=openai-codex/gpt-5.5` in `gateway.err.log`.
- Occasionally a single entry surfaces the underlying 400 rawError
above.
Workaround
Point active-memory's activeProvider/activeModel at any non-codex
provider (e.g. an lmstudio-served model). Confirms codex is the
isolated failure point.
Summary
When the
openai-codexprovider (apiopenai-codex-responses, baseUrlhttps://chatgpt.com/backend-api/codex) is invoked with a context whosemessagesarray is empty but whosesystemPromptis set, the resultingrequest body contains
input: []. ChatGPT's codex backend rejects this witha 400:
In practice the 400 closes the upstream socket before the streaming consumer
classifies the failure, so most calls surface to the user as a 15–31s
timeout rather than the underlying error — making this hard to diagnose
from logs alone. We caught the rawError on one occurrence:
with 27 sibling entries from the same workload showing
decision=surface_error reason=timeout from=openai-codex/gpt-5.5for whatappears to be the same root cause.
Environment
2026.4.26active-memory(memory-flush bootstrap run)openai-codex/gpt-5.4,openai-codex/gpt-5.5Root cause (suspected)
buildOpenAIResponsesParamsinprovider-stream-*.js:```js
const messages = convertResponsesMessages(model, context, ..., {
includeSystemPrompt: !isCodexResponses, // codex skips system msg
supportsDeveloperRole
});
const params = {
model: model.id,
input: messages, // empty when caller has no user turn
stream: true,
...isCodexResponses ? { instructions: buildOpenAICodexResponsesInstructions(context) } : {},
...
};
```
For codex, the system prompt is moved out of
messagesintoinstructions(correct per ChatGPT's codex contract). But if the caller passes
{ systemPrompt: "...", messages: [] }— which active-memory's flushbootstrap does —
params.inputends up as[]. The codex backendtreats that as missing and 400s.
Proposed fix
Inside
buildOpenAIResponsesParams, after theconvertResponsesMessagescall, ensure codex requests carry at least one input item when the only
content available is the system prompt:
```js
if (isCodexResponses && messages.length === 0 && context.systemPrompt) {
messages.push({
role: "user",
content: [{
type: "input_text",
text: sanitizeTransportPayloadText(
stripSystemPromptCacheBoundary(context.systemPrompt)
)
}]
});
}
```
Alternatives considered:
includeSystemPrompt: truefor codex unconditionally — wrong, theChatGPT codex backend expects system-as-instructions, not as a message.
messages.length === 0— surfaces a clearererror but doesn't restore functionality for the (legitimate) bootstrap case.
Repro
openai-codexprovider withgpt-5.5(or anyopenai-codex-responsesmodel).active-memoryplugin, setplugins.entries.active-memory.config.activeProvider = openai-codex,activeModel = gpt-5.5.crosses the flush threshold).
above.
Workaround
Point active-memory's
activeProvider/activeModelat any non-codexprovider (e.g. an
lmstudio-served model). Confirms codex is theisolated failure point.