headroomlabs-ai/headroom · error · Error

Headroom OpenCode wrap blocked direct HTTP/2 connection to $

Error message

Headroom OpenCode wrap blocked direct HTTP/2 connection to ${upstream.origin}. Use fetch, http, or https so traffic can be routed through Headroom.

What it means

Headroom's OpenCode hook shim monkey-patches Node's http2.connect. When OpenCode (or a dependency) opens a raw HTTP/2 session to an origin that shouldRoute() says belongs behind the Headroom proxy, the shim throws immediately instead of letting the connection bypass the proxy. This is intentional enforcement: traffic to routed upstreams must go through fetch/http/https, which the shim can transparently redirect to HEADROOM_OPENCODE_TRANSPORT_PROXY_URL.

Source

Thrown at headroom/providers/opencode/hook-shim/handler.js:305

    const nextArgs = parts.callback ? [nextOptions, parts.callback] : [nextOptions];
    return Reflect.apply(targetRequest, this, nextArgs);
  };
}
function wrapGet(request) {
  return function headroomGet(...args) {
    const req = Reflect.apply(request, this, args);
    req.end();
    return req;
  };
}
function wrapHttp2Connect(originalConnect) {
  return function headroomHttp2Connect(authority, ...args) {
    const state = getState();
    if (state) {
      const proxy = normalizeProxyUrl(state.proxyUrl);
      const upstream = authority instanceof URL ? authority : new URL(String(authority));
      if (shouldRoute(upstream, proxy)) {
        throw new Error(
          `Headroom OpenCode wrap blocked direct HTTP/2 connection to ${upstream.origin}. Use fetch, http, or https so traffic can be routed through Headroom.`
        );
      }
    }
    return Reflect.apply(originalConnect, this, [authority, ...args]);
  };
}
function installHeadroomTransport(options) {
  const existing = getState();
  if (existing) {
    existing.refs += 1;
    existing.proxyUrl = options.proxyUrl;
    existing.debug = Boolean(options.debug);
    installProcessEnv(options.proxyUrl);
    return () => uninstallHeadroomTransport();
  }
  const state = {
    refs: 1,

View on GitHub (pinned to 322425c43b)

Solutions

  1. Refactor the calling code to use fetch(), http.request, or https.request — those are wrapped and will be transparently routed through Headroom.
  2. If the direct HTTP/2 connection is legitimate and must not be proxied, narrow the routing scope so shouldRoute() returns false for that origin (configure the shim's proxy routing / bypass list accordingly).
  3. If you did not intend to run under Headroom at all, remove the shim preload (NODE_OPTIONS --require / import hook) from the process environment.

Example fix

// before
const session = http2.connect("https://api.anthropic.com"); // throws under shim

// after
const res = await fetch("https://api.anthropic.com/v1/messages", {...}); // routed through Headroom
Defensive patterns

Strategy: try-catch

Validate before calling

// Before any http2 use under the shim, check routing intent:
import http2 from "node:http2";
// no public shouldRoute export — instead probe with a sentinel try/catch once at startup (see tryCatchPattern).

Try / catch

function safeHttp2Connect(authority: string, ...args: unknown[]) {
  try {
    return http2.connect(authority, ...args);
  } catch (err) {
    if (err instanceof Error && err.message.includes("blocked direct HTTP/2 connection")) {
      // fall back to a routed transport
      return routeViaFetch(new URL(authority));
    }
    throw err;
  }
}

Prevention

When it happens

Trigger: Inside a process with the Headroom hook-shim preloaded, any code path calling http2.connect(authority) where authority's origin matches a routed upstream (shouldRoute(upstream, proxy) is true) — e.g. an SDK like @anthropic-ai/sdk or google-auth using HTTP/2 directly to api.anthropic.com or api.openai.com.

Common situations: An OpenCode plugin or MCP client switching to an HTTP/2-based transport (gRPC-style, node http2 core module) while running under `headroom opencode` wrapping; a dependency upgrade that moved from fetch/https to http2.connect; the proxy URL being set so broadly that every origin counts as routed.

Related errors


AI-assisted analysis of headroomlabs-ai/headroom@322425c43b (2026-08-15). Data as JSON: /api/errors/833d9d33eb7febd3. Report an issue: GitHub.