anomalyco / anomalyco/opencode

SDK v2 client: text/html interceptor throws on 204 No Content when a front-end (e.g. Cloud Run GFE) injects a default Content-Type, breaking every prompt_async

Open
#40,290 0 comments 0 reactions 1 assignee View on GitHub

@rekram1-node is already working on this.

Since Aug 3, 2026.

Dominant language
TypeScript
Stars
209k
Forks
27.5k
PR merge metrics
PR metrics pending

Description

Description

The v2 client in @opencode-ai/sdk throws on any response whose Content-Type is exactly text/html, regardless of HTTP status code:

// dist/v2/client.js — unchanged from at least 1.17.13 through 1.18.11 (latest as of 2026-08-03)
client.interceptors.response.use((response) => {
    const contentType = response.headers.get("content-type");
    if (contentType === "text/html")
        throw new Error("Request is not supported by this version of OpenCode Server (Server responded with text/html)");
    return response;
});

This check exists to catch the server's SPA fallback (200 + index.html) on unmatched routes, which is reasonable. But applying it to 204 No Content responses causes false positives: a 204 has no body, and the engine itself sends 204s (e.g. from POST /session/:id/prompt_async) with no Content-Type header at all. Some HTTP front-ends inject a default Content-Type: text/html (exactly, no charset) into responses that lack one. Google Cloud Run's front-end (GFE) does this — confirmed via browser DevTools showing server: Google Frontend and content-type: text/html on a 204 No Content from prompt_async, while a direct curl against the engine (bypassing the front-end) shows the same 204 with no Content-Type at all.

The result: when opencode serve runs behind Cloud Run (and likely other proxies/CDNs with the same default), every session.promptAsync() call throws this error client-side even though the prompt was accepted and processed successfully (204 = success). Reproduction is deterministic (100% of prompt sends).

Steps to reproduce

The front-end behavior is easy to simulate locally with a ~15-line proxy:

// gfe-sim.mjs — mimics Cloud Run's front-end: inject text/html when Content-Type is missing
import http from "node:http";
const [target, listen] = process.argv.slice(2).map(Number);
http.createServer((req, res) => {
  const proxy = http.request(
    { hostname: "127.0.0.1", port: target, path: req.url, method: req.method, headers: req.headers },
    (up) => {
      const headers = { ...up.headers };
      if (!headers["content-type"]) headers["content-type"] = "text/html";
      res.writeHead(up.statusCode, headers);
      up.pipe(res);
    },
  );
  req.pipe(proxy);
}).listen(listen, "127.0.0.1");
  1. opencode serve --port 4096
  2. node gfe-sim.mjs 4096 8099
  3. Point the SDK v2 client at the simulator and send a prompt:
import { createOpencodeClient } from "@opencode-ai/sdk/v2/client";
const client = createOpencodeClient({ baseUrl: "http://127.0.0.1:8099" });
const session = await client.session.create({});
await client.session.promptAsync({ sessionID: session.data.id, parts: [{ type: "text", text: "hello" }] });
// => throws: Request is not supported by this version of OpenCode Server (Server responded with text/html)

The same code pointed directly at :4096 succeeds. The engine's own response (direct curl) is:

HTTP/1.1 204 No Content
Vary: Origin
Date: ...
Content-Length: 0

(no Content-Type), and the prompt runs fine server-side in both cases — only the client-side interceptor fails.

Suggested fix

Exempt 204 from the text/html check (a 204 has no body, so its content-type can never be the SPA fallback this check is guarding against):

- if (contentType === "text/html")
+ if (contentType === "text/html" && response.status !== 204)
    throw new Error("Request is not supported by this version of OpenCode Server (Server responded with text/html)");

We've been running exactly this one-line change in a downstream fork of the pre-1.15.11 client since May without issues; after migrating to the published npm SDK we hit this again in production behind Cloud Run.

Environment

  • @opencode-ai/sdk: 1.17.13 (interceptor confirmed identical through 1.18.11)
  • opencode-ai (engine): 1.17.13
  • Deployment: opencode serve behind Google Cloud Run

Workaround

For anyone hitting this behind their own reverse proxy: rewrite Content-Type on 204 responses to application/json (or anything non-text/html) at the proxy layer before the front-end can inject its default. This resolved the failures for us, but it's deployment-specific — the SDK-side exemption above would fix it for everyone.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.