anomalyco / anomalyco/opencode
[Bug] GLM-5.2 prompt cache drops via custom-provider / reverse-proxy path (opencode + openclaw)
@nexxeln is already working on this.
Since Jul 29, 2026.
- Dominant language
- TypeScript
- Stars
- 209k
- Forks
- 27.5k
- PR merge metrics
- PR metrics pending
Description
Description
GLM-5.2 prompt cache drops to ~0/512 cached tokens within the same session, even though the system prompt is byte-identical across turns and the inter-call gap is well under the gateway's ~5 min TTL. This reproduces through both the OpenCode CLI and openclaw (a different client), when both are configured with a custom provider pointing at an OpenCode-Go / Zen-compatible endpoint. The bug is therefore not specific to the built-in opencode-go login path — it reproduces via any custom-provider / reverse-proxy path that fronts the Zen/Go GLM endpoint.
This is the same family of cache-drop behaviour tracked in #33998 and #37598, but reported from a custom-provider / reverse-proxy angle that none of the existing reports cover. The minimal reproducible evidence below is, to my knowledge, the cleanest longitudinal slice in the issue chain so far (8 calls / 10 min / 1 cache hit).
Longitudinal evidence — single session, ~10 minutes
All 8 requests below share the same session id ses_05486fd65ffexDmnAZ8dz9kEFh, the same model (glm-5.2), the same proxy, and the same client. Input tokens are within ±2k of each other (170k–172k). The gap between consecutive calls is always <2 min, well under the ~5 min TTL. Despite that, only 1 of 8 calls actually hit the prefix cache.
| Time (Asia/Shanghai) | Input tokens | Output tokens | Language & cost | Cache read |
|---|---|---|---|---|
| 10:47 | 172 707 | 295 | Go ($0.2431) | 0 |
| 10:46 | 172 207 | 37 | Go ($0.2413) | 0 |
| 10:45 | 170 995 | 86 | Go ($0.2392) | 512 |
| 10:41 | 171 513 | 360 | Go ($0.0464) | 171 328 ✅ |
| 10:40 | 171 438 | 68 | Go ($0.2403) | 0 |
| 10:40 | 171 349 | 42 | Go ($0.2401) | 0 |
| 10:39 | 170 085 | 971 | Go ($0.2420) | 384 |
| 10:37 | 169 901 | 1 491 | Go ($0.2444) | 0 |
Cost is the receipt that the upstream is genuinely doing more work: the single cache hit ($0.0464) is ~5× cheaper than the seven cache misses ($0.24 each). That is not "cache miss" rounding error — it is the full-input-vs-cached-input price differential. So this is not telemetry noise; the cache really is not being reused.
Why this is a "custom provider" report and not a duplicate of #33998
#33998 was filed from the direct built-in opencode-go login path, where the upstream gateway and the sticky-routing header path is straightforward to debug. My case is:
- I deliberately chose to not log in to the official
opencode-goprovider. (I'm aware of it — using custom-provider here is intentional, for proxy-side experimentation.) - I fronted the gateway with a local reverse proxy (
new-api) so I can inspect / reshape the request before it leaves the box. - The same symptom (cache dropped to 0 within the same session, byte-identical system prompt, gaps well under TTL) reproduces from both OpenCode and openclaw clients through that custom-provider path.
- That rules out client-side blame — both clients have entirely different request assembly pipelines (OpenCode uses
@ai-sdk/openai-compatible, openclaw uses its nativeopenai-completionstransport), yet both deliver requests that the Zen gateway fails to sticky-route on the GLM model.
This is consistent with the root-cause hypothesis already pinned in #33998 / #35402 (Zen gateway's stickyProvider not being enabled for GLM upstream pool, so selectProvider reshuffles the upstream and the prefix lands on a cold pool member). The new contribution here is two more independent client surfaces that exhibit the bug, and a per-call cost-impact number the maintainers can budget against.
Steps to reproduce
- Subscribe to OpenCode Go and get the gateway endpoint + key from the dashboard.
- Stand up any OpenAI-compatible reverse proxy in front of it (I used
new-apilocally; mitmproxy / nginx should work equally well for the cache-drop repro). - Configure both OpenCode and openclaw with a custom-provider entry pointing at the proxy:
- OpenCode:
provider.<name>.npm = "@ai-sdk/openai-compatible",options.baseURL = "http://<proxy>/v1",options.setCacheKey = true. - openclaw:
models.providers.<name>.api = "openai-completions",baseUrl = "http://<proxy>/v1",compat.supportsPromptCacheKey = true.
- OpenCode:
- Register
glm-5.2under that provider in both configs (sample configs below). - Open a single long-lived session in either client.
- Send 8+ conversational turns within ~10 minutes. Make small input variations only; keep the system prompt untouched after turn 1.
- Inspect the proxy / billing logs:
cached_tokenswill be near-zero on most turns and near-full on a minority — there is no clean monotonic climb, drops happen mid-session.
Plugins
- OpenCode side:
oh-my-openagent@latestonly (and the symptom reproduces regardless of whether this plugin is loaded, so it is not the cause). - openclaw side: no third-party plugins involved on this code path. The bug reproduces with a stock openclaw install, only the custom
wooh-openai-cprovider entry is required.
OpenCode version
v1.18.9 (latest stable as of 2026-07-28). Reproduced across the last 4–5 minor releases in this session, not a regression of one version.
OpenClaw version
2026.7.1-2 (lastTouchedVersion / lastTouchedAt 2026-07-20T14:25:57Z).
Screenshot and/or share link
- OpenCode Go dashboard usage log: 8 rows above (input / output / cost / cache-read columns), session
ses_05486fd65ffexDmnAZ8dz9kEFh, userh1@hihy.io. - Proxy-side trace available on request — happy to capture a fresh
mitmproxyflow if helpful, since themitmproxylog makes the cached-vs-miss turn boundary unambiguous.
Sample provider configs
~/.config/opencode/opencode.json (only the relevant provider block, baseURL / apiKey masked):
"wooh-openai-c": {
"name": "wooh-openai-c",
"npm": "@ai-sdk/openai-compatible",
"options": {
"apiKey": "sk-…REDACTED…",
"baseURL": "http://192.168.1.2:30000/v1",
"setCacheKey": true
},
"models": {
"glm-5.2": {
"name": "glm-5.2",
"limit": { "context": 1000000, "output": 128000 },
"modalities": { "input": ["text"] },
"reasoning": true
}
// …other models…
}
}
~/.openclaw/openclaw.json (only the relevant models.providers block, baseUrl / apiKey masked):
"models": {
"mode": "replace",
"providers": {
"wooh-openai-c": {
"baseUrl": "http://192.168.1.2:30000/v1",
"apiKey": "sk-…REDACTED…",
"api": "openai-completions",
"timeoutSeconds": 7200,
"models": [
{
"id": "glm-5.2",
"name": "glm-5.2",
"input": ["text"],
"contextWindow": 1000000,
"maxTokens": 128000,
"reasoning": true,
"compat": {
"supportsPromptCacheKey": true,
"supportsUsageInStreaming": true
}
}
// …other models…
]
}
// …other providers…
}
}
The proxy (http://192.168.1.2:30000) is a plain OpenAI-compatible passthrough — it adds no header rewriting, no session re-labelling, no model aliasing. Verified that without GLM in the picture the proxy preserves cache for DeepSeek V4 Flash / Kimi K3 / MiniMax M3 normally.
Operating System
macOS 15.7.7 (x86_64).
Terminal
iTerm2 (zsh — login shell).
Related issues / prior art
- #33998 — same symptom, original direct-
opencode-goreport by LittleNightmare; 10 comments, still open, root-cause traced to ZenstickyProvidernot enabled for GLM (#35402). - #37598 — companion bug: session id sometimes missing from OpenCode Go dashboard cache records.
- #35402 — root-cause analysis pinning the symptom on Zen gateway
selectProviderreshuffle for GLM.
I am not asking to supersede those issues — this report is meant to be a sibling that adds the custom-provider / reverse-proxy / cross-client angle and a clean 8-call / 1-hit longitudinal trace. Happy to cross-link and merge if the maintainers prefer a single thread.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Assessment
This issue has not been assessed yet.