Skip to content

v0.2.6: Built-in OpenCode Go provider doesn't send x-opencode-session (routing rejected since 2026-09-05) #13

Description

@moemcb

Title

v0.2.6: Built-in OpenCode Go provider doesn't send x-opencode-session (routing rejected since 2026-09-05)

Summary

OpenCode Go began enforcing the x-opencode-session header on 2026-09-05 to optimize routing and prompt caching (docs). Every request made through pentestcode v0.2.6's built-in opencode-go provider is now rejected with:

AI_APICallError: Error from provider (Console Go): Request is missing x-opencode-session and cannot be routed efficiently. Please see https://opencode.ai/docs/go/#where-can-i-use-it

Upstream OpenCode (v1.18.x) works against the same endpoint because it sends the header natively per session.

Reproduction

  1. Connect OpenCode Go (/connect), select any opencode-go/* model.
  2. Send any prompt.

Every stream fails with the routing error above (main agent requests and auxiliary requests alike).

Root cause

The embedded opencode-go provider (endpoint https://opencode.ai/zen/go/v1, internally displayed as "Console Go") never sets x-opencode-session. Per the Go docs, clients must send "a stable session ID in x-opencode-session for each conversation". The OpenCode session ID (ses_...) is the natural value, as upstream OpenCode already does.

Note: a static provider-level "headers" block in the config is not picked up for built-in providers, so users cannot work around this through config alone.

Suggested fix

Pass the current session ID as x-opencode-session on all requests to OpenCode providers (main and auxiliary paths) — the same behavior upstream OpenCode and validated clients (Hermes fix, jcode ≥ 0.81.6, Kilo Code PR #13752) now implement.

Workaround (verified working)

A local plugin that tracks the current session via the session.created / session.updated event hook and injects the header through an auth.loader custom fetch wrapper (pattern from opencode-helicone-session):

let currentSessionID = ""

export const GoSessionHeader = async () => ({
  auth: {
    provider: "opencode-go",
    methods: [],
    loader: async () => ({
      fetch: (url, init) => {
        const headers = new Headers(init?.headers)
        if (currentSessionID && !headers.has("x-opencode-session")) {
          headers.set("x-opencode-session", currentSessionID)
        }
        return fetch(url, { ...init, headers })
      },
    }),
  },
  event: async ({ event }) => {
    if (event.type === "session.created" || event.type === "session.updated") {
      currentSessionID = event.properties?.info?.id || currentSessionID
    }
  },
})

Tested on v0.2.6/Linux: requests to opencode-go/glm-5.3-flash succeed again, with the real per-conversation session ID sent in the header.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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