Description
The prompt_async endpoint (POST /:sessionID/prompt_async) wraps a fire-and-forget SessionPrompt.prompt() call in Hono's stream() helper. This opens a streaming HTTP response that never writes any data before closing. Behind reverse proxies with first-byte timeouts (Cloudflare's non-configurable 100s timeout on Free/Pro plans), this causes 524 A Timeout Occurred errors — even though the prompt was successfully accepted server-side.
The frontend then shows "Send failed" and reverts the input text, but the message was already processed. This creates a confusing UX where messages appear to fail but actually succeed.
Steps to reproduce
- Deploy OpenCode web behind Cloudflare CDN (Free or Pro plan)
- Send a message via the web UI
- If the streaming response doesn't complete fast enough, Cloudflare returns 524
- Frontend shows "Send failed" toast and reverts input to the text box
- But the message was actually received and processed by the server
Cloudflare tunnel error log (production)
ERR error="stream canceled by remote with error code 0" connIndex=2
ERR Request failed error="Incoming request ended abruptly: context canceled"
dest=https://example.com/session/ses_.../command
Root cause
In packages/opencode/src/server/routes/session.ts, the prompt_async handler:
async (c) => {
c.status(204)
c.header("Content-Type", "application/json")
return stream(c, async () => {
const sessionID = c.req.valid("param").sessionID
const body = c.req.valid("json")
SessionPrompt.prompt({ ...body, sessionID }) // fire-and-forget, no await, no stream.write()
})
},
The stream() wrapper opens a chunked transfer-encoding response but the callback never calls stream.write() — it just fires off the prompt and exits. This creates an empty streaming response that reverse proxies interpret as a stalled connection.
Expected behavior
prompt_async should return a direct 204 No Content response immediately, since it's designed to be fire-and-forget. An atomic HTTP response completes in milliseconds and is not susceptible to proxy timeout issues.
OpenCode version
1.2.17
Operating System
Ubuntu 24.04 (server behind Cloudflare Tunnel)
Description
The
prompt_asyncendpoint (POST /:sessionID/prompt_async) wraps a fire-and-forgetSessionPrompt.prompt()call in Hono'sstream()helper. This opens a streaming HTTP response that never writes any data before closing. Behind reverse proxies with first-byte timeouts (Cloudflare's non-configurable 100s timeout on Free/Pro plans), this causes 524 A Timeout Occurred errors — even though the prompt was successfully accepted server-side.The frontend then shows "Send failed" and reverts the input text, but the message was already processed. This creates a confusing UX where messages appear to fail but actually succeed.
Steps to reproduce
Cloudflare tunnel error log (production)
Root cause
In
packages/opencode/src/server/routes/session.ts, theprompt_asynchandler:The
stream()wrapper opens a chunked transfer-encoding response but the callback never callsstream.write()— it just fires off the prompt and exits. This creates an empty streaming response that reverse proxies interpret as a stalled connection.Expected behavior
prompt_asyncshould return a direct204 No Contentresponse immediately, since it's designed to be fire-and-forget. An atomic HTTP response completes in milliseconds and is not susceptible to proxy timeout issues.OpenCode version
1.2.17
Operating System
Ubuntu 24.04 (server behind Cloudflare Tunnel)