anomalyco / anomalyco/opencode

[Bug] GLM-5.2 prompt cache drops via custom-provider / reverse-proxy path (opencode + openclaw)

Open
#39,444 1 comment 0 reactions 1 assignee View on GitHub

@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-go provider. (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 native openai-completions transport), 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

  1. Subscribe to OpenCode Go and get the gateway endpoint + key from the dashboard.
  2. Stand up any OpenAI-compatible reverse proxy in front of it (I used new-api locally; mitmproxy / nginx should work equally well for the cache-drop repro).
  3. 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.
  4. Register glm-5.2 under that provider in both configs (sample configs below).
  5. Open a single long-lived session in either client.
  6. Send 8+ conversational turns within ~10 minutes. Make small input variations only; keep the system prompt untouched after turn 1.
  7. Inspect the proxy / billing logs: cached_tokens will 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@latest only (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-c provider 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, user h1@hihy.io.
  • Proxy-side trace available on request — happy to capture a fresh mitmproxy flow if helpful, since the mitmproxy log 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-go report by LittleNightmare; 10 comments, still open, root-cause traced to Zen stickyProvider not 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 selectProvider reshuffle 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

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.