nanobot began sending the x-opencode-session header in v0.3.5, published September 15, 2026; releases up to v0.3.0 sent only a generic x-session-affinity id, which is the version gap behind the missing-session-header failure against OpenCode Go. Run nanobot --version before changing anything else: if it reports v0.3.0 or older, the upgrade is the whole fix for that one header. What the upgrade does not settle is the rest of OpenCode Go's client contract, and that is the part worth reading before you conclude the route is healthy.
One disambiguation first, because the names collide. OpenCode Go here is the live $10/month model subscription documented at opencode.ai/docs/go — not the archived Go-language OpenCode terminal agent, whose own README says the project continued under the name Crush with the original author and the Charm team. This is not a migration note about a retired product; it is a troubleshooting page about a currently sold service that tightened a client requirement. And nanobot here is HKUDS/nanobot, the MIT-licensed Python personal-agent framework — 48,459 stars, not archived, last pushed on the day of this check (GitHub API, September 21, 2026) — not the Go project of the same name at obot-platform.
The error, and which nanobot versions carry it
The notice that started this reached subscribers as a provider notification, not as documentation. A user quoted it verbatim in anomalyco/opencode#47438, opened September 5, 2026 against OpenCode's own client and describing it as a notification from the model provider that arrived by email: "Some of your requests to OpenCode Go are missing an x-opencode-session header. If we don't have this we cannot properly optimize our service. Starting 09/06 requests missing this header may error." That wording appears in no OpenCode documentation we could find. OpenCode's changelog, read September 21, 2026, covers August 4 to September 14, 2026 at v1.18.31 and contains no entry mentioning x-opencode-session at all. The Go documentation phrases it as a request, not a rejection: "Send a stable session ID in x-opencode-session for each conversation so we can optimize routing and prompt caching."
The 09/06 date itself does have an official public source, just not a documentation one: nanobot issue #5661 and PR #5662 both cite an @opencode post on X dated September 3, 2026, which they quote as saying that tools missing the header lose prompt-cache optimization and that "Starting 09/06 requests missing this header may error." That post is the announcement the nanobot fix was written against; this page has not independently retrieved it.
The hard failure is evidenced only by third parties. vastsa/PI-Desktop#48, opened and closed on September 7, 2026, records HTTP 400 with "type": "MissingSessionID" and a message saying the request is missing x-opencode-session and cannot be routed efficiently, pointing at the Go docs anchor. OpenCode's Go documentation carries no error reference listing that status code or that type string, so read the documented obligation and the reported rejection as two different strengths of claim from the same vendor.
| nanobot version | Sends x-opencode-session? | Evidence |
|---|---|---|
| v0.3.0 (July 25, 2026) and earlier | No — a generic per-process x-session-affinity id only | Issue #5661, opened September 4, 2026, closed September 9 |
main after September 9, 2026 | Yes | Commit 20f115bf, merged from PR #5662 |
| v0.3.5 (September 15, 2026) | Yes | The v0.3.5 release notes list that PR |
There is no v0.3.1 through v0.3.4 in the tag history, so "v0.3.0 and earlier" is the whole affected range. Two checks confirmed the release mapping on September 21, 2026 rather than inferring it: the release body lists PR #5662 by number, and a GitHub compare call reports v0.3.5 as 84 commits ahead of 20f115bf and zero behind it, which puts the commit inside the tag. Note that nanobot's v0.3.5 documentation never mentions the header — the only records of the change are the release notes, the PR and the issue.
What the fix actually does, and what triggers it
Read at tag v0.3.5 in nanobot/providers/openai_compat_provider.py, the logic is small and worth knowing precisely. nanobot treats a target as an OpenCode target when the provider spec is named opencode, opencode_zen or opencode_go, or when the base URL's hostname is opencode.ai or a subdomain of it. When there is conversation context, the header value is the SHA-256 hex digest of the session id, which keeps it opaque and ASCII-safe for non-ASCII ids. When there is no conversation context, the provider instance falls back to a random UUID fixed for its own lifetime — stable for that instance, not per conversation. A header you configured yourself wins over both, matched case-insensitively on the name.
The hostname clause is the sentence to carry away: the header is attached because of where the request is going, not because of which provider block you wrote. A generic providers.custom block pointed at an opencode.ai base URL still gets it. A request to any other host does not get it — what that host requires instead is its own vendor's question, and nothing here answers it.
Upgrading is ordinary: the PyPI package is nanobot-ai, at 0.3.5 in that tag's pyproject.toml, and the v0.3.5 README documents uv tool install nanobot-ai and python -m pip install nanobot-ai. Confirm with nanobot --version, or python -m nanobot --version if the entry point is not on PATH, then nanobot status, which the CLI reference describes as checking provider and model readiness without calling a model.
Why OpenCode Go is not a plain OpenAI-compatible endpoint
This is the part that makes a header fix feel incomplete. OpenCode Go states several client-side conditions that an ordinary OpenAI-compatible base URL does not, and the session header is only one of them. All rows below come from its own documentation, checked September 21, 2026.
| Requirement | What OpenCode Go asks for | Settled by upgrading to v0.3.5? |
|---|---|---|
| Subscription | "OpenCode Go is a low cost $10/month subscription"; the key comes from subscribing, then /connect in the TUI | No — a separate purchase |
| Session header | A stable session ID in x-opencode-session for each conversation | Yes |
| User agent | "Identify itself with its own user agent, such as my-coding-agent/1.0, rather than a generic SDK or HTTP-library name" | Not demonstrated — see below |
| Usage windows | A per-model monthly dollar limit, with a 5-hour sub-limit at 20% and a weekly one at 50% | No |
| Endpoint per model | Three surfaces — /zen/go/v1/responses, /chat/completions and /messages — so the model decides the wire format | No, and nanobot narrows it further |
| Traffic shape | "designed for OpenCode and other coding agents that produce similar types of requests", with traffic monitored for abuse | No |
The user-agent row deserves the caution. PR #5662 changed the session header only. Reading nanobot's v0.3.5 source, the OpenAI-compatible provider module — the one that serves the opencode, opencode_zen and opencode_go specs — sets no User-Agent anywhere, while the GitHub Copilot, xAI Grok and OpenAI Codex provider modules each set a nanobot-branded one. So on that path the request carries whatever the underlying SDK sends by default. No request was put on the wire here to observe the value, and OpenCode has published no enforcement for that line, so this is a reading of source rather than a reproduced failure. The documented remedy is a header you set yourself — nanobot's reference describes providers.<name>.extraHeaders as headers merged into provider requests:
{
"providers": {
"opencodeGo": {
"apiKey": "${OPENCODE_API_KEY}",
"extraHeaders": { "User-Agent": "nanobot/0.3.5" }
}
},
"modelPresets": {
"primary": {
"provider": "opencode_go",
"model": "opencode-go/<a model OpenCode lists under chat/completions>",
"maxTokens": 8192,
"contextWindowTokens": 65536
}
}
}Name your own client truthfully there. Putting one of OpenCode's validated client names in that field would be impersonation, not identification, and it is not what the documentation asks for.
The endpoint row has a nanobot-specific consequence that no header fixes. Its v0.3.5 provider reference says to use model IDs that OpenCode lists under the chat/completions endpoint, because models listed only under responses, messages or provider-specific endpoints are not handled by that OpenAI-compatible path. In config, OpenCode Go is providers.opencodeGo with a preset whose provider is opencode_go, and model ids carry an opencode-go/ prefix that nanobot strips before sending. Finally, OpenCode's Go page publishes a list of validated clients (Hermes, Claude Code, Codex, ZCode, Pi, jcode and Kilo Code CLI) and a list with missing or incomplete session support (DeepSeek Harness, GitHub Copilot Chat, Kimi Code and MiMo Code). nanobot is on neither list as of September 21, 2026 — an absence of a published verdict, not an endorsement and not a block. That page also notes per-client version requirements which were not captured here, so do not read a minimum version for any of those clients from this page.
Verify with one redacted request
nanobot's v0.3.5 documentation describes no way to print its outgoing headers, so confirm the contract directly against your own subscription before concluding anything about the client. Read only the status line.
# Read the status line only. Key redacted; session id is your own, stable per conversation.
curl -sS -o /dev/null -D - https://opencode.ai/zen/go/v1/chat/completions \
-H "authorization: Bearer $OPENCODE_API_KEY" \
-H "x-opencode-session: $(printf 'my-conversation-1' | shasum -a 256 | cut -d' ' -f1)" \
-H "user-agent: nanobot/0.3.5" \
-H "content-type: application/json" \
-d '{"model":"<model-id>","messages":[{"role":"user","content":"ping"}],"max_tokens":8}'That separates two failures cleanly: a 400 naming MissingSessionID is the header, and anything else is not the header. Do not rotate a fresh random id per request to make the error disappear, and do not borrow another client's name. The documentation asks for a stable id per conversation precisely because routing and prompt caching key on it, so a per-request id defeats the caching you are paying for while looking like a fix. The two legitimate answers are to upgrade, or to move that workload to a route that does not carry the requirement.
What this costs, and which route wins
Keep the software separate from the tokens. nanobot itself is $0 — MIT-licensed and self-hosted, per the repository record read on September 21, 2026 — so everything below is the model bill and the machine it runs on.
| Route | How it bills | What you give up |
|---|---|---|
| OpenCode Go subscription | $10/month, then a per-model monthly dollar cap with 5-hour and weekly sub-limits at 20% and 50% of it | Two client obligations, three per-model endpoints, and a stated intended-traffic boundary a general personal-agent framework may sit outside |
| OpenCode Zen | Pay-as-you-go per 1M tokens; card fees passed at cost (4.4% + $0.30 per transaction); balance below $5 auto-reloads $20 | A separate product from Go with its own rate card; free models carry a stated data-use caveat |
| Direct vendor API | The vendor's own per-token rate | A second vendor means a second key and a second preset |
| An OpenAI-compatible gateway | Metered tokens, one key and one balance, no subscription | You pick from that gateway's catalog rather than Go's list; OpenCode's session-header requirement is scoped to opencode.ai and nanobot does not stamp the header elsewhere |
| Local model | No per-request charge; Ollama, vLLM and LM Studio are built-in nanobot providers | Hardware, and a capability gap against hosted frontier models |
The Zen row comes from its own documentation, read September 21, 2026; Zen and Go are separately billed products, so a Zen rate is not what a Go subscriber pays, and Zen's own list marks several models as temporarily free while providers gather feedback. OpenCode pricing covers the client's own cost side.
The subscription cannot be converted into the table below, and pretending otherwise is the easiest mistake here. Go's allowance is denominated in dollars at Go's own per-model rates, and each model carries its own cap: the documentation's worked example is a $60 monthly model allowing $12 of usage per 5 hours and $30 per week, while the model rows read on September 21, 2026 show GLM-5.3-Flash at $0.15 input and $0.50 output per 1M with a $60 monthly limit and an estimated 31,580 requests a month. Those are OpenCode's estimates for its own catalogue, not guarantees, and one row carried a dated promotion that expires shortly after this was written. For a workload that fits Go's model list and its windows, $10 buys a lot of metered usage; the comparison is really about whether your models and your bursts fit inside it.
A metered gateway prices the same workload differently. These are illustrative token arithmetic, not measured task costs and not a bill ceiling: assume one nanobot assistant consuming 3,000,000 uncached input and 300,000 output tokens in a month, at live Kunavo catalog rates per million tokens.
| Model | Input / output per 1M | Estimate for the assumed month |
|---|---|---|
| Claude Haiku 4.5 | $0.40 / $2.00 | $1.80 |
| Gemini 3.8 Flash | $0.525 / $2.625 | $2.36 |
| Claude Sonnet 5 | $2.00 / $10.00 | $9.00 |
| Claude Opus 5 | $2.00 / $10.00 | $9.00 |
Scale those by your own traffic before treating them as a budget, and note that a background schedule can move the input column far more than the model choice does — nanobot API cost and setup works through the cadence nanobot enables by default. Kunavo's catalog amount is a billing floor rather than a cap: when the upstream reports its charge, the bill is the greater of catalog cost and upstream cost times the applicable markup. Cache charges, tools and hosting sit outside this example, and the minimum top-up is $10 in prepaid credit — a funding minimum, not a task fee or a subscription. See billing details.
Where Kunavo does and does not help
Being blunt about this matters more than a conversion. Kunavo does not resell OpenCode Go or Zen, and pointing nanobot at https://api.kunavo.com/v1 does not repair an OpenCode Go subscription. It avoids the requirement instead, because x-opencode-session belongs to opencode.ai hosts — and by the hostname rule in v0.3.5's source, nanobot will not stamp that header at any other base URL either. What Kunavo's endpoint would do with such a header has not been tested here, and neither has nanobot against Kunavo: there is no nanobot integration page, and every configuration statement above is a reading of vendor documentation and shipped source rather than a compatibility result.
So treat the routes as alternatives with different trade-offs, not as a fix and a workaround. If a metered OpenAI-compatible route suits the workload, the quickstart covers the endpoint convention and creating a Kunavo account is the step before funding a key — keep a working route available while you try it, run one bounded task, then read what your account recorded for it. If you arrived looking for the archived Go terminal agent rather than the subscription, the Crush integration doc covers its successor, and the OpenCode integration doc covers the current TypeScript client. For a wider comparison, see OpenAI-compatible API and the agent API directory; for a different OpenCode failure, provider or model not found is the diagnostic.
FAQ
Which nanobot version sends the x-opencode-session header?
v0.3.5, published September 15, 2026, is the first release that contains it. The change is PR #5662 in HKUDS/nanobot, merged September 9, 2026 at commit 20f115bf4699bffcc786263cb999e7701986e179, and the v0.3.5 release notes list it. GitHub's tag history shows no v0.3.1 through v0.3.4 — the previous tag is v0.3.0 from July 25, 2026 — so the affected range is v0.3.0 and earlier. A git checkout of main taken after September 9, 2026 also has the change without being on a tagged release. Checked against the GitHub API on September 21, 2026, including a compare call showing v0.3.5 is 84 commits ahead of that commit and zero behind it.
Why does nanobot get a 400 MissingSessionID from OpenCode Go?
Because releases up to v0.3.0 sent only a generic per-process x-session-affinity id and never the conversation-scoped x-opencode-session header that OpenCode Go asks for. That is how the originating nanobot issue #5661 describes the behaviour, and the generic header is still visible in the v0.3.5 provider source alongside the new one. The status code and the error type string come from a third-party bug report, vastsa/PI-Desktop#48 of September 7, 2026, which shows HTTP 400 with type MissingSessionID and the message that the request cannot be routed efficiently. OpenCode's Go documentation carries no error reference listing that code or that type, so treat the exact contract as user-reported rather than documented.
Can I set the x-opencode-session header manually instead of upgrading nanobot?
You can, and the v0.3.5 source honours it — a header you configure yourself takes precedence, matched case-insensitively on the name — but it is the wrong shape of fix. nanobot's providers.<name>.extraHeaders is documented as headers merged into provider requests, so one static value would go out on every request from that provider block, for every conversation. OpenCode Go's documentation asks for a stable session ID for each conversation so it can optimize routing and prompt caching, so a single shared value, or a fresh random value per request, works against the caching you are paying for. Upgrade to v0.3.5, or move that workload to a route that does not carry the requirement.
Does upgrading to nanobot v0.3.5 make it fully compatible with OpenCode Go?
It settles the session header and nothing else, and OpenCode Go states more than one client obligation. Its documentation also asks that a client identify itself with its own user agent, such as my-coding-agent/1.0, rather than a generic SDK or HTTP-library name. Reading nanobot's v0.3.5 source, the OpenAI-compatible provider module that serves the OpenCode providers sets no User-Agent at all, while the GitHub Copilot, xAI Grok and OpenAI Codex provider modules each set a nanobot-branded one — so on that path the request carries whatever the underlying SDK sends by default. No request was put on the wire here to confirm what that value is. nanobot's own v0.3.5 provider reference adds a second limit: use model IDs that OpenCode lists under the chat/completions endpoint, because models listed only under responses, messages or provider-specific endpoints are not handled by that path. OpenCode's Go documentation lists seven validated clients and four with missing or incomplete session support, and nanobot is on neither list as of September 21, 2026 — an absence of a published verdict, not an endorsement and not a block.
Is OpenCode Go the same thing as the old OpenCode Go CLI?
No, and conflating them sends you to the wrong documentation. OpenCode Go is a live $10/month model subscription sold at opencode.ai, serving models over https://opencode.ai/zen/go/v1/ endpoints and publishing a client contract its documentation asks callers to meet. The archived Go-language OpenCode terminal agent is a separate project; its own README says the project has continued under the name Crush, developed by the original author and the Charm team. That one is client software, not a model service, and it has nothing to do with the x-opencode-session header. If you landed here looking for the terminal agent, Kunavo's Crush integration doc covers that one.
Does routing nanobot at a different gateway fix the error?
It avoids the error rather than fixing it, and the distinction matters. The x-opencode-session requirement belongs to opencode.ai hosts specifically. In nanobot v0.3.5 the header is attached based on where the request is going: the provider spec is named opencode, opencode_zen or opencode_go, or the base URL hostname is opencode.ai or a subdomain of it. Point a provider block at any other host and nanobot sends no such header; whatever that host requires instead is its own vendor's contract, which this page does not cover. That is a different route with different models and different billing, not a repair of an OpenCode Go subscription you have already paid for.
Checked September 21, 2026: opencode.ai's Go and Zen documentation and its changelog; the GitHub API for HKUDS/nanobot releases, tags, PR #5662, issue #5661 and a compare call proving the commit is inside the v0.3.5 tag; anomalyco/opencode#47438 and vastsa/PI-Desktop#48 for the quoted notice and the reported 400; and the v0.3.5 source tarball for every claim about what nanobot sends. The 09/06 date traces to an @opencode post on X of September 3, 2026 that nanobot's issue and PR quote but this page did not retrieve; the quoted subscriber notice and the 400 error contract are user-reported, and neither appears in OpenCode's documentation or changelog. Nobody here has run nanobot against OpenCode Go or against Kunavo, or reproduced the failure; Kunavo token rates come from the live catalog and every dollar figure is illustrative arithmetic at the stated assumptions.