Bug
The catch-all API proxy at apps/web/src/app/api/opencode/[port]/[[...path]]/route.ts uses request.body (a ReadableStream) to forward POST/PUT/PATCH request bodies. In Next.js 16 running on Bun, request.body can return null when the body has already been consumed by the framework, or the stream may not be correctly forwarded.
Root Cause
// route.ts:121-133
let body: ReadableStream | null = null
if (["POST", "PUT", "PATCH"].includes(request.method)) {
body = request.body // ❌ may be null in Next.js 16/Bun
}
const response = await fetch(targetUrl, {
method: request.method,
headers,
body,
duplex: body ? "half" : undefined, // Node.js-only option, not supported by Bun
})
Two problems:
request.body may be null — Next.js route handlers don't always preserve the raw ReadableStream
duplex: "half" is a Node.js-specific fetch option — Bun's native fetch may ignore it or behave differently
Result: The backend receives a POST with an empty body, fails to parse JSON, returns 400.
Additional Bug: 204 No Content crash
After forwarding the request, the proxy calls response.text() for ALL responses, including 204 No Content (which has no body):
// route.ts:148
const responseBody = await response.text() // ❌ 204 has no body
This causes a crash on 204 responses (like successful prompt_async responses).
Fix
Read body as text string instead of streaming:
let body: string | undefined
if (["POST", "PUT", "PATCH"].includes(request.method)) {
body = await request.text()
}
And handle 204 explicitly:
if (response.status === 204) {
return new NextResponse(null, { status: 204 })
}
Also fixed the utils/prompt-api.ts convertToApiParts to use crypto.randomUUID() consistently.
Impact
All POST/PUT/PATCH requests via the proxy (which is ALL client<->backend communication on mobile/Tailscale) silently get empty bodies.
Bug
The catch-all API proxy at
apps/web/src/app/api/opencode/[port]/[[...path]]/route.tsusesrequest.body(aReadableStream) to forward POST/PUT/PATCH request bodies. In Next.js 16 running on Bun,request.bodycan returnnullwhen the body has already been consumed by the framework, or the stream may not be correctly forwarded.Root Cause
Two problems:
request.bodymay benull— Next.js route handlers don't always preserve the raw ReadableStreamduplex: "half"is a Node.js-specific fetch option — Bun's native fetch may ignore it or behave differentlyResult: The backend receives a POST with an empty body, fails to parse JSON, returns 400.
Additional Bug: 204 No Content crash
After forwarding the request, the proxy calls
response.text()for ALL responses, including 204 No Content (which has no body):This causes a crash on 204 responses (like successful
prompt_asyncresponses).Fix
Read body as text string instead of streaming:
And handle 204 explicitly:
Also fixed the
utils/prompt-api.tsconvertToApiPartsto usecrypto.randomUUID()consistently.Impact
All POST/PUT/PATCH requests via the proxy (which is ALL client<->backend communication on mobile/Tailscale) silently get empty bodies.