`responses` corrupts previous conversations `assistant` item
The role=system messages are red herrings. I could switch them all to role=developer and it wouldn’t make a difference. It could omit them and it still wouldn’t make a difference. I also tried switching the hard coded assistant messages from EasyInputMessage to a ResponseOutputMessage, that didn’t make a difference either.
It seems like all hard coded assistant messages synthetically inserted into a Conversation using the conversations/conversation_id/items POST endpoint, prior to a responses call that produces an AI generated message will be wiped as soon as the response from that call is returned.
The only way to synthetically insert an assistant message into a Conversation is to create it by making a deterministic call to responses where the message is presented to the AI with a request to simply reply with the exact message. It feels a little hacky in my opinion, but it seems to be the only thing that works. Something like this:
{
"model": "gpt-5.4-mini",
"conversation": "conv_...",
"temperature": 0,
"input": [
{
"role": "developer",
"content": "Reply EXACTLY with the following text, no deviation:\n\n[HARD CODED ASSISTANT MESSAGE]"
},
{
"role": "user",
"content": "Start"
}
]
}
To wrap up, here’s an extended bash script that inserts three hard coded assistant messages with different phases, followed by a user message, and then a Responses call, and all the assistant messages get wiped.
#!/usr/bin/env bash
set -eu
: "${OPENAI_API_KEY:?Set OPENAI_API_KEY first}"
BASE="https://api.openai.com/v1"
MODEL="gpt-5.4-mini"
AUTH=(
-H "Authorization: Bearer $OPENAI_API_KEY"
-H "Content-Type: application/json"
)
SYSTEM_TEXT="System message containing instructions."
USER_TEXT="Initial user message"
SECOND_USER_TEXT="Second user message before Responses call"
DEVELOPER_TEXT="Has the user provided answers to the outstanding fields? "
list_items() {
curl -sS --globoff \
"$BASE/conversations/$CONV_ID/items?order=asc&limit=100" \
"${AUTH[@]}"
}
retrieve_item() {
ITEM_ID="$1"
curl -sS --globoff \
"$BASE/conversations/$CONV_ID/items/$ITEM_ID" \
"${AUTH[@]}"
}
echo "1) Create conversation with system message"
CREATE_CONV_RESPONSE=$(
curl -sS "$BASE/conversations" \
"${AUTH[@]}" \
-d "$(jq -n --arg text "$SYSTEM_TEXT" '{
items: [
{
role: "system",
content: $text,
type: "message"
}
]
}')"
)
echo "$CREATE_CONV_RESPONSE" | jq .
CONV_ID=$(echo "$CREATE_CONV_RESPONSE" | jq -r '.id')
echo "CONV_ID=$CONV_ID"
echo
echo "2) Add first user message"
curl -sS "$BASE/conversations/$CONV_ID/items" \
"${AUTH[@]}" \
-d "$(jq -n --arg text "$USER_TEXT" '{
items: [
{
type: "message",
role: "user",
content: [
{
type: "input_text",
text: $text
}
]
}
]
}')" | jq .
echo
echo "3) Add assistant messages with different phase variants"
ASSISTANT_IDS=()
for PHASE in "final_answer" "commentary" ""; do
LABEL="${PHASE:-none}"
TEXT="Assistant (${LABEL}) message"
echo "Adding assistant message with phase: ${LABEL}"
ADD_ASSISTANT_RESPONSE=$(
curl -sS "$BASE/conversations/$CONV_ID/items" \
"${AUTH[@]}" \
-d "$(jq -n \
--arg text "$TEXT" \
--arg phase "$PHASE" \
'{
items: [
(
{
type: "message",
role: "assistant",
content: [
{
type: "output_text",
text: $text
}
]
}
+ (if $phase != "" then {phase: $phase} else {} end)
)
]
}')"
)
echo "$ADD_ASSISTANT_RESPONSE" | jq .
ID=$(echo "$ADD_ASSISTANT_RESPONSE" | jq -r '.data[0].id')
ASSISTANT_IDS+=("$ID")
done
echo
echo "4) Add SECOND user message"
curl -sS "$BASE/conversations/$CONV_ID/items" \
"${AUTH[@]}" \
-d "$(jq -n --arg text "$SECOND_USER_TEXT" '{
items: [
{
type: "message",
role: "user",
content: [
{
type: "input_text",
text: $text
}
]
}
]
}')" | jq .
echo
echo "5) List all items BEFORE Responses call"
LIST_BEFORE=$(list_items)
echo "$LIST_BEFORE" | jq .
echo
echo "6) Verify assistant items exist BEFORE Responses"
for ID in "${ASSISTANT_IDS[@]}"; do
if echo "$LIST_BEFORE" | jq -e --arg id "$ID" '.data[] | select(.id == $id)' >/dev/null; then
echo "OK: $ID present"
else
echo "ERROR: $ID missing before Responses"
fi
done
echo
echo "7) Create response using SAME conversation"
CREATE_RESPONSE_RESPONSE=$(
curl -sS "$BASE/responses" \
"${AUTH[@]}" \
-d "$(jq -n \
--arg model "$MODEL" \
--arg conv "$CONV_ID" \
--arg text "$DEVELOPER_TEXT" \
'{
model: $model,
conversation: $conv,
store: true,
input: [
{
role: "developer",
type: "message",
content: $text
}
]
}')"
)
echo "$CREATE_RESPONSE_RESPONSE" | jq .
echo
echo "8) List all items AFTER Responses call"
LIST_AFTER=$(list_items)
echo "$LIST_AFTER" | jq .
echo
echo "9) Check assistant messages after Responses"
for ID in "${ASSISTANT_IDS[@]}"; do
echo
echo "Checking $ID"
if echo "$LIST_AFTER" | jq -e --arg id "$ID" '.data[] | select(.id == $id)' >/dev/null; then
echo "❌ Still present in list"
else
echo "✅ Missing from list (DISAPPEARED)"
fi
RETRIEVE=$(retrieve_item "$ID")
echo "$RETRIEVE" | jq .
if echo "$RETRIEVE" | jq -e '
.role == "unknown"
and (.content[0].text == "<unknown message>")
' >/dev/null; then
echo "✅ Corrupted to <unknown message>"
else
echo "❌ Still valid"
fi
done
echo
echo "Done."
echo "CONV_ID=$CONV_ID"
echo "ASSISTANT_IDS=${ASSISTANT_IDS[*]}"
I am sure they have a very good reason for why they don’t allow synthetic assistant messages in a conversation, but it is a little frustrating either way. Thank you all for your help figuring this one out!
Discussion in the ATmosphere