Skip to content

Bug: API proxy doesn't forward POST body in Bun/Next.js 16 #20

@drewsephski

Description

@drewsephski

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:

  1. request.body may be null — Next.js route handlers don't always preserve the raw ReadableStream
  2. 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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions