Before submitting
Related: #4239. That issue's title/body is about OPENCODE_CONFIG_CONTENT="{}" clobbering user config. This report is about the separate intermittent inventory loss described in its second comment, filed separately so it can be triaged on its own. I can reproduce it deterministically and have narrowed the mechanism.
Area
apps/server
Problem
T3 Code's cached OpenCode inventory contains a contiguous prefix of the models OpenCode actually reports. Everything past the cut is silently dropped, and the provider still reports ready with a healthy "4 upstream providers connected" message.
On my machine 79 of 186 models are missing, including every recent OpenAI model (openai/gpt-5.5, openai/gpt-5.6, openai/gpt-5.6-luna, openai/gpt-5.6-sol, openai/gpt-5.6-terra). Restarting T3 Code restores them until a later refresh drops them again.
Steps to reproduce
- Enable only the OpenCode provider (no external server URL configured, so the CLI inventory path at
OpenCodeProvider.ts:394 is used).
- Use an OpenCode install with a large model inventory (mine reports 186 models across 4 upstream providers).
- Let a scheduled provider refresh run.
- Compare the cache against the live CLI:
$ opencode models --verbose | grep -cE '^\S+/\S+$'
186
$ jq '.models | length' ~/.t3/caches/opencode.json
107
Expected behavior
The picker offers every model OpenCode reports, or the provider surfaces an error when the inventory could not be fully read.
Actual behavior
107 of 186 models are cached. The loss is a clean positional split, not a random subset.
Per upstream provider:
| provider |
live CLI |
T3 cache |
| anthropic |
10 |
10 |
| bsp |
3 |
3 |
| gemini |
30 |
30 |
| openai |
143 |
64 |
| total |
186 |
107 |
Mapping each cached slug back to its position in opencode models --verbose output order:
- present: positions 1–107
- missing: positions 108–186
- models in the cache that are not in the live list: 0
So the cache is exactly the first 107 entries of the CLI output. openai/gpt-5.6-sol sits at roughly position 114, just past the cut.
The cut is not registry staleness. The dropped tail includes long-standing models that certainly existed when the snapshot was written:
openai/o1
openai/o3
openai/text-embedding-ada-002
openai/tts-1
openai/whisper-1
It is also not the empty-name skip at OpenCodeProvider.ts:230. The dropped models have valid names:
$ opencode models --verbose | grep -A 8 '^openai/gpt-5.6-sol$'
openai/gpt-5.6-sol
{
"id": "gpt-5.6-sol",
...
"status": "active",
"name": "GPT-5.6 Sol",
The CLI itself is stable — three consecutive runs each returned 186. The truncation happens in T3 Code's capture of the subprocess stdout. In the current output, position 108 begins at byte 111,805 of 191,445, so roughly the last 40% of the stream is lost.
Why it fails silently
runOpenCodeCommand (opencodeRuntime.ts:397-409) reads stdout with collectStreamAsString, which calls collectUint8StreamText without maxBytes, so the cap defaults to POSITIVE_INFINITY. There is no intentional limit — the stream is ending early, which looks like the stdout pipe not being fully drained before the child's exit resolves. 191 KB across a 64 KB pipe buffer needs several reads.
Three separate behaviors then conspire to hide it:
parseModelsCliOutput swallows the truncated trailing JSON block in a bare catch {} (opencodeRuntime.ts:209-211), so the tell-tale parse failure is discarded.
connected is not reported by OpenCode on this path — it is synthesized as [...providers.keys()] (opencodeRuntime.ts:229), i.e. derived from whatever happened to parse. A truncated read still yields all 4 providers here, because each provider's first models land early in the stream, so the snapshot looks healthy.
- The retry only fires on a failed spawn or non-zero exit (
opencodeRuntime.ts:678). A truncated-but-exit-0 read is accepted as authoritative and overwrites the previously good cache.
Net effect: status: "ready", "4 upstream providers connected through OpenCode.", and a third of the inventory quietly gone.
Impact
Major degradation or frequent failure
Models cannot be selected at all while truncated, and which models disappear shifts with inventory size and ordering, so it reads as random flakiness to users.
Version or commit
0.0.32-nightly.20260803.986
Environment
macOS 26.5.2, T3 Code Nightly desktop 0.0.32-nightly.20260803.986, OpenCode 1.18.11, OpenCode-only provider via the CLI inventory path (no external server URL).
Logs
~/.t3/caches/opencode.json at the time of capture:
{
"instanceId": "opencode",
"driver": "opencode",
"enabled": true,
"installed": true,
"version": "1.18.11",
"status": "ready",
"checkedAt": "2026-08-03T20:05:43.172Z",
"message": "4 upstream providers connected through OpenCode.",
"auth": { "status": "authenticated", "type": "opencode" },
"modelCount": 107
}
Suggested fixes
- Fully drain stdout before treating the command as complete, so the result cannot depend on a pipe-drain race.
- Treat an unparseable trailing model block as a hard error instead of skipping it — on this path it is a reliable truncation signal.
- Do not replace a larger cached inventory with a materially smaller one from a single probe without corroboration.
- Longer term, prefer the SDK
provider.list path over parsing opencode models --verbose stdout, so connected is authoritative rather than synthesized from parse success.
Workaround
Restart T3 Code to force a fresh probe. It holds until the next refresh truncates again.
Before submitting
Related: #4239. That issue's title/body is about
OPENCODE_CONFIG_CONTENT="{}"clobbering user config. This report is about the separate intermittent inventory loss described in its second comment, filed separately so it can be triaged on its own. I can reproduce it deterministically and have narrowed the mechanism.Area
apps/server
Problem
T3 Code's cached OpenCode inventory contains a contiguous prefix of the models OpenCode actually reports. Everything past the cut is silently dropped, and the provider still reports
readywith a healthy "4 upstream providers connected" message.On my machine 79 of 186 models are missing, including every recent OpenAI model (
openai/gpt-5.5,openai/gpt-5.6,openai/gpt-5.6-luna,openai/gpt-5.6-sol,openai/gpt-5.6-terra). Restarting T3 Code restores them until a later refresh drops them again.Steps to reproduce
OpenCodeProvider.ts:394is used).Expected behavior
The picker offers every model OpenCode reports, or the provider surfaces an error when the inventory could not be fully read.
Actual behavior
107 of 186 models are cached. The loss is a clean positional split, not a random subset.
Per upstream provider:
Mapping each cached slug back to its position in
opencode models --verboseoutput order:So the cache is exactly the first 107 entries of the CLI output.
openai/gpt-5.6-solsits at roughly position 114, just past the cut.The cut is not registry staleness. The dropped tail includes long-standing models that certainly existed when the snapshot was written:
It is also not the empty-name skip at
OpenCodeProvider.ts:230. The dropped models have valid names:The CLI itself is stable — three consecutive runs each returned 186. The truncation happens in T3 Code's capture of the subprocess stdout. In the current output, position 108 begins at byte 111,805 of 191,445, so roughly the last 40% of the stream is lost.
Why it fails silently
runOpenCodeCommand(opencodeRuntime.ts:397-409) reads stdout withcollectStreamAsString, which callscollectUint8StreamTextwithoutmaxBytes, so the cap defaults toPOSITIVE_INFINITY. There is no intentional limit — the stream is ending early, which looks like the stdout pipe not being fully drained before the child's exit resolves. 191 KB across a 64 KB pipe buffer needs several reads.Three separate behaviors then conspire to hide it:
parseModelsCliOutputswallows the truncated trailing JSON block in a barecatch {}(opencodeRuntime.ts:209-211), so the tell-tale parse failure is discarded.connectedis not reported by OpenCode on this path — it is synthesized as[...providers.keys()](opencodeRuntime.ts:229), i.e. derived from whatever happened to parse. A truncated read still yields all 4 providers here, because each provider's first models land early in the stream, so the snapshot looks healthy.opencodeRuntime.ts:678). A truncated-but-exit-0 read is accepted as authoritative and overwrites the previously good cache.Net effect:
status: "ready","4 upstream providers connected through OpenCode.", and a third of the inventory quietly gone.Impact
Major degradation or frequent failure
Models cannot be selected at all while truncated, and which models disappear shifts with inventory size and ordering, so it reads as random flakiness to users.
Version or commit
0.0.32-nightly.20260803.986
Environment
macOS 26.5.2, T3 Code Nightly desktop 0.0.32-nightly.20260803.986, OpenCode 1.18.11, OpenCode-only provider via the CLI inventory path (no external server URL).
Logs
~/.t3/caches/opencode.jsonat the time of capture:{ "instanceId": "opencode", "driver": "opencode", "enabled": true, "installed": true, "version": "1.18.11", "status": "ready", "checkedAt": "2026-08-03T20:05:43.172Z", "message": "4 upstream providers connected through OpenCode.", "auth": { "status": "authenticated", "type": "opencode" }, "modelCount": 107 }Suggested fixes
provider.listpath over parsingopencode models --verbosestdout, soconnectedis authoritative rather than synthesized from parse success.Workaround
Restart T3 Code to force a fresh probe. It holds until the next refresh truncates again.