Error running remote compact task: stream disconnected before completion: error sending request for url (https://chatgpt.com/backend-api/codex/responses/compact)
Nothing failed in the turn you were working on. A maintenance operation failed —
the client was asking the platform to summarize your conversation so the session
could keep going, and that request never completed. The trailing URL says so
explicitly: the path ends in /compact, a different endpoint from the one your
ordinary turns use. So this is not an auth failure, not a quota rejection and
not a malformed prompt; all three of those come back as documented HTTP statuses
with a type and a code in the body, and none of them mention a URL.
Read the message from the inside out
Three failures are stacked in one line, and only the innermost one carries a fact.
The outermost layer, “Error running remote compact task”, names which operation died. The middle layer, “stream disconnected before completion”, is the client’s generic wrapper for any streaming call that ended without finishing — it appears on half a dozen unrelated failures and tells you almost nothing on its own. The innermost layer, “error sending request for url”, is the only part describing what actually happened at the wire: the HTTP client could not deliver the request to that URL.
That last distinction is worth slowing down for, because it rules out most of
what you would otherwise investigate. OpenAI’s streaming semantics are split
into two documented halves: HTTP error responses apply before the stream
starts, and an error arriving after streaming begins comes through as a stream
event carrying its own code and message. This message is neither. There
was no status to read and no error event to parse, because the request leg
itself failed — the connection could not be established, or it died before a
response began. Nothing on the platform’s side ever formed an opinion about your
request.
Why compaction is the request that breaks first
Here is the judgment that changes what you do next. A compaction request carries the entire conversation as its input, which makes it the single largest and longest-lived request the session will ever issue. Ordinary turns send an incremental slice and start streaming tokens back quickly. The compaction call uploads everything you have accumulated and then waits while the model reads all of it before producing the summary.
Anything on the path that is sensitive to request size, upload duration, or time to first byte will therefore trip on the compaction call while every ordinary turn in the same session continues to succeed. That is not a coincidence you should explain away as bad luck — it is a selection effect, and it is the reason this specific endpoint shows up in the message.
The consequence is worse than a single failed operation. Compaction exists to bring the conversation back under the ceiling. When it fails, the session stays at its maximum size, so the next ordinary turn is now also a worst-case request, and the next compaction attempt re-sends the same oversized payload over the same path that just refused it. Each attempt is simultaneously the most expensive request available and the most likely to fail. That is a loop that tightens rather than resolves, and it commonly ends with your input exceeds the context window — a hard, deterministic rejection — rather than with a recovered session.
Is retrying useful?
No. Not as your response to this message.
Re-running compaction sends a byte-identical payload — the conversation has not changed — down a path that has already demonstrated it cannot carry it. If the cause is size or duration, the second attempt reproduces the first exactly. If the cause is a transient network fault, the retry may work, but you cannot tell those two apart by retrying, which is the whole problem.
There is also nothing below you that will retry on your behalf. The official Python client’s documentation states plainly that stream consumption is not automatically retried, because replaying a request could duplicate output already delivered to the application. Whatever retry count your client displays is a retry it implemented itself.
If you want one attempt as a measurement, take exactly one, and decide in advance what each outcome means: success means transient and you move on; an identical failure means deterministic and you stop. Failed requests still count against your per-minute limits, so a compaction loop is not free — and it is burning the largest request you have on a known outcome.
Which hop actually refused it
Each of these has an observation that settles it.
Do ordinary turns still work in the same session? This is the first test and it costs you one message. If normal streaming turns succeed and only compaction fails, the path is not broken — something on it is sensitive to the size or duration that only compaction reaches. If ordinary turns fail too, you have a general transport problem and the compaction wrapper is a red herring; read the request-sending failure at the responses endpoint, which covers the same innermost error on the ordinary endpoint.
Does it fail after a consistent interval? Time it across several attempts. A stable elapsed time before the error means a timer somewhere fired — a read or idle timeout on a proxy, a gateway, or your own HTTP client. A wandering time means genuine network variance.
Did it start after a network change? A VPN, a new office gateway, a container image bump, a laptop resuming from sleep. Corporate egress appliances that inspect or buffer request bodies sit in exactly the position to fail a large upload while letting small ones through. Reproduce from a different network; this is the cheapest test here and the most frequently skipped.
Does the session size predict it? Note roughly how large the conversation was on each failure. If compaction succeeds early in a session and fails reliably once the conversation grows, you have confirmed the size-sensitivity explanation and the fix is to stop letting the conversation reach that size.
Fix by scenario
- Only compaction fails, ordinary turns are fine — do not re-run it. Reduce what compaction has to carry: end the session and start a new one seeded with a short summary you write yourself. A summary you paste in is a few hundred tokens; the request that just failed was the whole history.
- Consistent elapsed time before failure — raise the read and idle timeouts at every hop on the path, then verify each from the running configuration rather than the file you edited. Fixing one of three timers is the usual reason the change appears to do nothing.
- Everything fails, not just compaction — treat it as a transport problem and stop reasoning about context at all.
- Only on one network — the appliance in the middle is the suspect. Test the same session from a different path before you change anything in your tooling.
- It keeps happening at the same point in every long session — the real fix is structural: work in shorter sessions, or plan the budget before you hit the ceiling with the context window calculator rather than discovering the limit through a failed compaction.
How to confirm it’s fixed
The confirmation has to control for the variable that was failing, which is size, not correctness. A compaction that succeeds on a small session proves nothing — small sessions were never failing.
Run compaction on a conversation at least as large as the one that failed, and require it to complete twice in a row on the same network path. If you changed a timeout, record the elapsed time of the successful run and confirm it exceeds the interval that used to kill the request; a run that finished faster than the old failure point has not tested your change.
Related errors
The neighbouring failures in this cluster are told apart by whether the ceiling was reached or the transport gave up. If the client eventually prints the overflow verdict itself, you are holding your input exceeds the context window, which is deterministic and has nothing to do with the network — that page is about the arithmetic, this one is about the delivery. The same verdict arrives as a clean HTTP rejection when a request is refused up front rather than mid- flight; see the 400 context_length_exceeded response for what that looks like when nothing is wrapping it. And when the output ceiling rather than the input ceiling is squeezing you, maxTokens is too close to contextLength covers the case where the two numbers leave no room to answer in.