INTEGRATION TROUBLESHOOTING

Handle OpenAI Responses stream errors after partial output

Keep partial text separate from a completed answer, and reproduce an explicit stream error or a silent early end.

Provider documentation checked 8 November 2026. Examples below use fixed synthetic fixtures; no request runs from this page.

A few text deltas do not make a completed answer

A client opens an SSE connection, renders some text and marks the task successful when its read loop ends. That conflates three outcomes: completed output, an explicit in-stream failure, and an ordinary EOF before completion. HTTP 200 only tells you that the streaming response started successfully.

Provider contract: OpenAI's HTTP streaming guide uses stream: true with typed SSE events. Its Responses event reference distinguishes response.output_text.delta, response.completed, error, response.failed and response.incomplete. A delta is progress, not terminal success.

Client policy: commit the answer only after validating a successful response-level completion for the same response. A text-part or output-item done event is not a substitute. Keep interim text uncommitted, or clearly labelled partial if you show it before completion.

Select an error after partial text, then inspect it

  1. In an existing workspace, install OpenAI Responses API. Select Stream Response → Error event after partial text (openai-stream-error). Use the synchronous streaming operation; no background mode, polling or callback setup is needed.
  2. Copy your installed receiver URL into local test configuration. Replace <your-receiver-token> in the inert sample below with your private receiver token. Keep the synthetic bearer value; no real OpenAI API key or model call is required.
  3. Send the stream request. Observe its HTTP status and content type first, then retain the ordered SSE events and the client's final classification. Do not interpret curl's successful process exit as a completed model response.
  4. Inspect the installed module's captured request: POST path, synthetic headers, JSON input and stream: true. Compare the selected HTTP response with your client-side event log. A captured HTTP 200 alone cannot prove that your client processed a terminal event.
  5. Repeat after explicitly selecting Interrupted stream, then Text lifecycle and completed response. The same request and receiver are used; changing the selected behaviour changes the fixed event sequence.

Stream Response: Error event after partial text

Representative Synthetic illustrative example from the catalogue, not a live capture. Run only after installing your own receiver. Replace <your-receiver-token> locally; no request is sent from this page.

HTTP 200 · SSE · Stream error after partial output

curl / Bash request

printf '%s' '{"model":"gpt-4.1","input":"Say hello","stream":true}' | \
curl -i --no-buffer -X POST \
  'https://nottherealapi.com/r/<your-receiver-token>/v1/responses' \
  -H 'Authorization: Bearer synthetic-openai-token' \
  -H 'Content-Type: application/json' \
  --data-binary @-

PowerShell request

$body = '{"model":"gpt-4.1","input":"Say hello","stream":true}'
Invoke-WebRequest `
    -UseBasicParsing `
    -Uri 'https://nottherealapi.com/r/<your-receiver-token>/v1/responses' `
    -Method POST `
    -Headers @{
    Authorization = 'Bearer synthetic-openai-token'
    } `
    -ContentType 'application/json' `
    -Body ([Text.Encoding]::UTF8.GetBytes($body))

Selected synthetic response

HTTP 200
content-type: text/event-stream; charset=utf-8

event: response.created
data: {"type":"response.created","response":{"id":"resp_example_001","object":"response","created_at":1792238400,"model":"gpt-4.1","status":"in_progress","error":null,"incomplete_details":null,"output":[],"parallel_tool_calls":true,"tools":[],"tool_choice":"auto","metadata":{},"usage":null},"sequence_number":0}

event: response.in_progress
data: {"type":"response.in_progress","response":{"id":"resp_example_001","object":"response","created_at":1792238400,"model":"gpt-4.1","status":"in_progress","error":null,"incomplete_details":null,"output":[],"parallel_tool_calls":true,"tools":[],"tool_choice":"auto","metadata":{},"usage":null},"sequence_number":1}

event: response.output_item.added
data: {"type":"response.output_item.added","output_index":0,"item":{"id":"msg_example_001","type":"message","role":"assistant","status":"in_progress","content":[]},"sequence_number":2}

event: response.content_part.added
data: {"type":"response.content_part.added","item_id":"msg_example_001","output_index":0,"content_index":0,"part":{"type":"output_text","text":"","annotations":[],"logprobs":[]},"sequence_number":3}

event: response.output_text.delta
data: {"type":"response.output_text.delta","item_id":"msg_example_001","output_index":0,"content_index":0,"delta":"Hello ","logprobs":[],"sequence_number":4}

event: error
data: {"type":"error","code":"server_error","message":"Synthetic mid-stream failure","param":null,"sequence_number":5}

Events above are the fixed fixture sequence. Delivery timing is simulated; see the full behaviour list for completion, error and early EOF choices.

The curl/Bash command includes --no-buffer so curl does not deliberately buffer its output. On Windows, run that command in Git Bash with curl. The PowerShell Invoke-WebRequest sample uses the same UTF-8 JSON, but buffers the response: it is useful for inspecting the final payload, not proving incremental delta handling. Use the Bash command or your SSE-aware client for that test.

The selected error stream is Representative. Its error message and precise sequence are this simulator's example, not a promise that every live OpenAI failure uses the same payload or timing.

Test completion, explicit failure and silent early end

One operation, three separately selected streams
BehaviourSelected wire outcomeWhat to assert
Text lifecycle and completed response
openai-stream
HTTP 200 · SSE · Stream completes
Documented
Commit success only after a matching response.completed whose response status is completed. Text or item-level done events are not enough.
Error event after partial text
openai-stream-error
HTTP 200 · SSE · Stream error after partial output
Representative
Return a failed result on the explicit error event. Keep already received text labelled partial; do not commit it as a completed answer.
Interrupted stream
openai-stream-cut
HTTP 200 · SSE · Interrupted before completion
Synthetic
Clean EOF without a successful completion is incomplete, even without a socket or HTTP error. Keep partial text separate from success.

These are the current catalogue's exact event names and order, derived from the selected fixtures. Each event's SSE event name agrees with the JSON data's type. The clean-EOF fixture simply stops after its last listed delta: it adds no terminal marker and does not simulate a TCP reset.

Text lifecycle and completed response: ordered fixture events
  1. response.created
  2. response.in_progress
  3. response.output_item.added
  4. response.content_part.added
  5. response.output_text.delta
  6. response.output_text.delta
  7. response.output_text.done
  8. response.content_part.done
  9. response.output_item.done
  10. response.completed
HTTP 200
content-type: text/event-stream; charset=utf-8

event: response.created
data: {"type":"response.created","response":{"id":"resp_example_001","object":"response","created_at":1792238400,"model":"gpt-4.1","status":"in_progress","error":null,"incomplete_details":null,"output":[],"parallel_tool_calls":true,"tools":[],"tool_choice":"auto","metadata":{},"usage":null},"sequence_number":0}

event: response.in_progress
data: {"type":"response.in_progress","response":{"id":"resp_example_001","object":"response","created_at":1792238400,"model":"gpt-4.1","status":"in_progress","error":null,"incomplete_details":null,"output":[],"parallel_tool_calls":true,"tools":[],"tool_choice":"auto","metadata":{},"usage":null},"sequence_number":1}

event: response.output_item.added
data: {"type":"response.output_item.added","output_index":0,"item":{"id":"msg_example_001","type":"message","role":"assistant","status":"in_progress","content":[]},"sequence_number":2}

event: response.content_part.added
data: {"type":"response.content_part.added","item_id":"msg_example_001","output_index":0,"content_index":0,"part":{"type":"output_text","text":"","annotations":[],"logprobs":[]},"sequence_number":3}

event: response.output_text.delta
data: {"type":"response.output_text.delta","item_id":"msg_example_001","output_index":0,"content_index":0,"delta":"Hello ","logprobs":[],"sequence_number":4}

event: response.output_text.delta
data: {"type":"response.output_text.delta","item_id":"msg_example_001","output_index":0,"content_index":0,"delta":"there.","logprobs":[],"sequence_number":5}

event: response.output_text.done
data: {"type":"response.output_text.done","item_id":"msg_example_001","output_index":0,"content_index":0,"text":"Hello there.","logprobs":[],"sequence_number":6}

event: response.content_part.done
data: {"type":"response.content_part.done","item_id":"msg_example_001","output_index":0,"content_index":0,"part":{"type":"output_text","text":"Hello there.","annotations":[],"logprobs":[]},"sequence_number":7}

event: response.output_item.done
data: {"type":"response.output_item.done","output_index":0,"item":{"id":"msg_example_001","type":"message","role":"assistant","status":"completed","content":[{"type":"output_text","text":"Hello there.","annotations":[],"logprobs":[]}]},"sequence_number":8}

event: response.completed
data: {"type":"response.completed","response":{"id":"resp_example_001","object":"response","created_at":1792238400,"model":"gpt-4.1","status":"completed","error":null,"incomplete_details":null,"output":[{"id":"msg_example_001","type":"message","role":"assistant","status":"completed","content":[{"type":"output_text","text":"Hello there.","annotations":[],"logprobs":[]}]}],"parallel_tool_calls":true,"tools":[],"tool_choice":"auto","metadata":{},"usage":{"input_tokens":10,"output_tokens":3,"total_tokens":13,"input_tokens_details":{"cached_tokens":0},"output_tokens_details":{"reasoning_tokens":0}}},"sequence_number":9}

Error event after partial text: ordered fixture events
  1. response.created
  2. response.in_progress
  3. response.output_item.added
  4. response.content_part.added
  5. response.output_text.delta
  6. error
HTTP 200
content-type: text/event-stream; charset=utf-8

event: response.created
data: {"type":"response.created","response":{"id":"resp_example_001","object":"response","created_at":1792238400,"model":"gpt-4.1","status":"in_progress","error":null,"incomplete_details":null,"output":[],"parallel_tool_calls":true,"tools":[],"tool_choice":"auto","metadata":{},"usage":null},"sequence_number":0}

event: response.in_progress
data: {"type":"response.in_progress","response":{"id":"resp_example_001","object":"response","created_at":1792238400,"model":"gpt-4.1","status":"in_progress","error":null,"incomplete_details":null,"output":[],"parallel_tool_calls":true,"tools":[],"tool_choice":"auto","metadata":{},"usage":null},"sequence_number":1}

event: response.output_item.added
data: {"type":"response.output_item.added","output_index":0,"item":{"id":"msg_example_001","type":"message","role":"assistant","status":"in_progress","content":[]},"sequence_number":2}

event: response.content_part.added
data: {"type":"response.content_part.added","item_id":"msg_example_001","output_index":0,"content_index":0,"part":{"type":"output_text","text":"","annotations":[],"logprobs":[]},"sequence_number":3}

event: response.output_text.delta
data: {"type":"response.output_text.delta","item_id":"msg_example_001","output_index":0,"content_index":0,"delta":"Hello ","logprobs":[],"sequence_number":4}

event: error
data: {"type":"error","code":"server_error","message":"Synthetic mid-stream failure","param":null,"sequence_number":5}

Interrupted stream: ordered fixture events
  1. response.created
  2. response.in_progress
  3. response.output_item.added
  4. response.content_part.added
  5. response.output_text.delta
HTTP 200
content-type: text/event-stream; charset=utf-8

event: response.created
data: {"type":"response.created","response":{"id":"resp_example_001","object":"response","created_at":1792238400,"model":"gpt-4.1","status":"in_progress","error":null,"incomplete_details":null,"output":[],"parallel_tool_calls":true,"tools":[],"tool_choice":"auto","metadata":{},"usage":null},"sequence_number":0}

event: response.in_progress
data: {"type":"response.in_progress","response":{"id":"resp_example_001","object":"response","created_at":1792238400,"model":"gpt-4.1","status":"in_progress","error":null,"incomplete_details":null,"output":[],"parallel_tool_calls":true,"tools":[],"tool_choice":"auto","metadata":{},"usage":null},"sequence_number":1}

event: response.output_item.added
data: {"type":"response.output_item.added","output_index":0,"item":{"id":"msg_example_001","type":"message","role":"assistant","status":"in_progress","content":[]},"sequence_number":2}

event: response.content_part.added
data: {"type":"response.content_part.added","item_id":"msg_example_001","output_index":0,"content_index":0,"part":{"type":"output_text","text":"","annotations":[],"logprobs":[]},"sequence_number":3}

event: response.output_text.delta
data: {"type":"response.output_text.delta","item_id":"msg_example_001","output_index":0,"content_index":0,"delta":"Hello ","logprobs":[],"sequence_number":4}

Keep the success boundary explicit

This executable JavaScript example consumes an async iterable of already decoded SSE JSON data objects. Feed it events from a compliant SSE parser or your client's decoded-event interface. It is not a raw-byte parser: a network chunk is not necessarily a whole event. Check HTTP status and content type before entering this boundary.

async function collectResponse(events) {
  let responseId = null;
  let partialText = "";
  try {
    // events yields decoded SSE data objects, not raw network chunks.
    for await (const event of events) {
      if (event.type === "response.created") {
        responseId = event.response.id;
      } else if (event.type === "response.output_text.delta") {
        partialText += event.delta;
      } else if (event.type === "error" ||
                 event.type === "response.failed" ||
                 event.type === "response.incomplete") {
        return { ok: false, kind: event.type, partialText };
      } else if (event.type === "response.completed") {
        const response = event.response;
        if (!responseId || response.id !== responseId ||
            response.status !== "completed" ||
            response.error != null || response.incomplete_details != null) {
          return { ok: false, kind: "invalid-completion", partialText };
        }
        return { ok: true, kind: "completed", text: partialText };
      }
    }
  } catch {
    return { ok: false, kind: "read-error", partialText };
  }
  return { ok: false, kind: "early-eof", partialText };
}

The example handles the guide's single text output. It does not assemble arbitrary output items, validate every event schema, enforce deadlines or dispatch tools. Add those responsibilities in your client. Test a read/decoder exception separately from clean EOF; both remain unsuccessful, but only the former is a read error. The current mock's cutoff exercises the latter.

response.failed and response.incomplete are additional documented non-success events handled by the example; these three streaming fixtures do not emit them. A pre-stream HTTP error belongs to your HTTP error handler, not the success-stream path. Do not blindly replay a failed generation or execute actions from partial output: make retry and partial-display policy explicit.

Scope, fidelity and sources

The streaming guide and event reference linked above were checked on 8 November 2026. This article covers Responses HTTP SSE at /v1/responses, using the module's fixed v1 fixtures. It does not describe Realtime/WebSocket events, Anthropic Messages termination or Chat Completions' stream markers.

Fixture model names, IDs, usage values, text and timing are illustrative. No model generates output, no tool runs, and selected failures do not measure account spend or rate limits. Completion is labelled Documented, the error sequence Representative, and early EOF Synthetic; none is a live provider capture. The mock does not autonomously recover between these tests.

Background Responses, polling and signed callbacks are separate lifecycle features with their own deployment enablement. For the complete supported surface, see the OpenAI Responses mock API reference. For another case where transport success is misread, see Dataverse bodyless 204 response handling.

Continue with your integration test

Read the OpenAI Responses mock API reference for the full operation and behaviour catalogue. Compare workspace pricing and limits.

Start 7-day free trial

No card required. Your trial starts on workspace creation, with no automatic charge. Subscribing during the trial charges immediately and consumes remaining free time.

Browse all developer guides · Dataverse 204 No Content and OData identity handling

OpenAI Responses Streaming Errors & Early EOF | NotTheRealAPI