OpenAI GPT-3.5 upsert operation timing out during real-time streaming

Problem: OpenAI GPT‑3.5 upsert operation timing out during real‑time streaming

In a production streaming pipeline, data is continuously fed into the OpenAI Chat Completions or Embeddings endpoint with stream=true. After a few seconds of normal operation the upsert request aborts with a timeout, causing downstream processing stalls and loss of inference results.

Typical log excerpt

2026-06-03T12:34:45.123Z ERROR OpenAIError: The server timed out while processing the request.
2026-06-03T12:34:45.124Z INFO  Request ID: req_7f3a9b2c
2026-06-03T12:34:45.125Z WARN  stream closed unexpectedly after 15872 bytes
2026-06-03T12:34:45.126Z ERROR Error: timeout of 60000ms exceeded

Symptoms include:

  • Interrupted streaming responses after 30‑60 seconds.
  • HTTP 429 or 503 errors interleaved with timeout messages.
  • Reduced throughput in downstream Kafka or Pub/Sub consumers.

Root Cause Analysis

The timeout can be attributed to three interacting factors:

  1. Payload size limits – The OpenAI API enforces a 16 KB per‑message limit for streaming payloads (Chat Completions docs). Large batch upserts (e.g., >1000 documents) exceed this limit, forcing the server to abort the stream.
  2. Server‑side processing window – The API imposes a 60‑second processing ceiling. When the cumulative token count approaches the 4 K token limit per request, the server spends >60 s generating the response and returns The server timed out while processing the request. (see the official error handling guide).
  3. Network keep‑alive mismatch – Many corporate firewalls reset idle connections after 30 s. OpenAI’s streaming heartbeat is ~10 s, but if the client disables TCP keep‑alive or the proxy strips the heartbeat, the connection is terminated, surfacing as a client‑side timeout (error‑codes guide).

Real‑world incidents confirm this triad:

  • Fintech Kafka‑to‑OpenAI connector hit a 30 s latency spike when message size exceeded 16 KB.
  • E‑commerce batch upserts of >1000 documents triggered HTTP 429 throttling and subsequent timeouts.
  • Healthcare pipeline suffered read ECONNRESET due to firewall idle‑timeout shorter than OpenAI’s 60 s keep‑alive.

Investigation and Debugging

1. Capture request/response payload sizes

curl -s -D - -X POST https://api.openai.com/v1/chat/completions \
  -H "Authorization: Bearer $OPENAI_API_KEY" \
  -H "Content-Type: application/json" \
  -d @payload.json -o /dev/null | grep -i content-length

Expected output for a compliant request:

Content-Length: 1432

2. Inspect streaming heartbeat

tcpdump -i eth0 -nn -s0 -w stream.pcap host api.openai.com and port 443

Search for HTTP/2 ping frames every ~10 s. Missing pings indicate a keep‑alive break.

3. Verify token usage

python -c "
import tiktoken
enc = tiktoken.get_encoding('cl100k_base')
print('tokens:', len(enc.encode(open('payload.json').read())) )"

If token count > 4000, the request will be split.

4. Check client timeout configuration

# Node.js example
const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY, timeout: 120_000 });

Default client timeout is 60 000 ms; increasing it can hide server‑side timeouts but not fix root causes.

Solution

1. Chunk large upserts

Break batch upserts into ≤1000‑document chunks and ensure each chunk’s JSON payload stays under 16 KB.

Before

# payload.json (single massive batch)
{
  "model": "gpt-3.5-turbo",
  "messages": [... 2500 documents ...],
  "stream": true
}

After

# chunked_upsert.py
import json, math, requests

MAX_DOCS = 800   # keeps payload < 16 KB
def chunk_docs(docs):
    for i in range(0, len(docs), MAX_DOCS):
        yield docs[i:i+MAX_DOCS]

def upsert_chunk(chunk):
    payload = {
        "model": "gpt-3.5-turbo",
        "messages": chunk,
        "stream": True
    }
    resp = requests.post(
        "https://api.openai.com/v1/chat/completions",
        headers={"Authorization": f"Bearer {os.getenv('OPENAI_API_KEY')}",
                 "Content-Type": "application/json"},
        json=payload,
        timeout=120_000,
        stream=True)
    for line in resp.iter_lines():
        if line:
            print(line.decode())

# usage
with open('docs.json') as f:
    docs = json.load(f)
for chunk in chunk_docs(docs):
    upsert_chunk(chunk)

2. Enforce token limits per request

Trim max_tokens or split the conversation when cumulative tokens approach 4 K.

# Python example
MAX_TOKENS = 3500
if token_count > MAX_TOKENS:
    # split context
    context_a, context_b = split_context(messages)
    upsert_chunk(context_a)
    upsert_chunk(context_b)

3. Align keep‑alive settings

Enable TCP keep‑alive and send periodic heartbeat pings on the client side.

# Node.js with openai-node SDK
const client = new OpenAI({
  apiKey: process.env.OPENAI_API_KEY,
  timeout: 120_000,
  httpAgent: new http.Agent({ keepAlive: true, keepAliveMsecs: 15000 })
});

4. Implement exponential back‑off for rate‑limit responses

# Retry wrapper
def call_openai(payload):
    for attempt in range(5):
        resp = requests.post(url, json=payload, timeout=120_000)
        if resp.status_code == 429:
            wait = 2 ** attempt
            time.sleep(wait)
            continue
        resp.raise_for_status()
        return resp

Verification

  • Monitor stream closed unexpectedly log entries – they should disappear after chunking.
  • Confirm payload size via Content-Length header stays < 16 KB for every request.
  • Validate token count stays below 4 K using the tiktoken script.
  • Observe steady heartbeat frames in tcpdump captures (one ping every 10 s).
  • Run a load test (e.g., 500 messages/s) and verify no timeout or HTTP 429/503 responses for at least 10 minutes.

Prevention and Best Practices

Practice Why it helps
Enforce payload size < 16 KB Avoids server‑side abort due to message size limit (Fintech incident).
Limit per‑request token count to ≤3500 Prevents hitting the 60 s processing ceiling.
Chunk batch upserts into ≤800 documents Provides headroom for JSON overhead and keeps payload small.
Enable TCP keep‑alive & client heartbeat Prevents firewall idle‑timeout resets (Healthcare pipeline).
Implement exponential back‑off on 429/503 Respects OpenAI rate‑limit guidance and reduces throttling spikes.
Instrument metrics: request latency, token count, payload size Early detection of trend toward timeout thresholds.

Related Topic Hub: LLM Systems Troubleshooting Hub

FAQ

  1. Why does the stream timeout only after a few seconds of normal operation?
    Because the request payload exceeds the 16 KB limit, causing the server to buffer and eventually abort once the internal buffer overflows, which typically manifests after 30‑60 s of streaming.
  2. Can increasing the client‑side timeout resolve the issue?
    Only the symptom of client‑side timeout of 60000ms exceeded is hidden; the underlying server‑side processing limit (60 s) and payload limits remain unchanged, so the request will still fail.
  3. How do I know if I’m hitting the token limit?
    Use the tiktoken library to count tokens before sending. If the count approaches 4 K, split the conversation or reduce max_tokens in the payload.
  4. What HTTP status code indicates rate‑limit throttling?
    HTTP 429 Too Many Requests, often accompanied by a Retry-After header. Treat it as a transient condition and back off exponentially.
  5. Is there a way to keep the stream alive without chunking?
    No. The OpenAI API enforces hard size limits; the only reliable method is to respect those limits by chunking or reducing context size.