Repository navigation
Description
Description
Azure deployment discovery runs once when the provider starts (and on account switch). If that request fails, it's logged and never retried, so the model list stays catalog-only even after the network comes back, until the provider is restarted (e.g. opencode service restart).
- Failure is logged and dropped:
opencode/packages/core/src/plugin/provider/azure.ts
Lines 308 to 315 in 4d59657
const found = yield* deployments(url, name, credential).pipe( // Azure promises no order; normalize it so a reordered response does not rebuild the model list. Effect.map((list) => list.toSorted((a, b) => a.name.localeCompare(b.name))), Effect.catch((cause) => Effect.logWarning("failed to sync Azure deployments", { cause }).pipe(Effect.as(undefined)), ), ) if (!found) return - Discovery only runs at start and on account switch:
opencode/packages/core/src/plugin/provider/azure.ts
Lines 410 to 418 in 4d59657
// A switch interrupts discovery for the previous connection instead of waiting for its Azure calls. yield* bus.subscribe(Credential.Event.Switched).pipe( Stream.filter((event) => event.data.integrationID === Integration.ID.make("azure")), Stream.runForEach(() => refresh()), Effect.forkScoped({ startImmediately: true }), ) // Deployments load in the background so startup never waits on Azure; the catalog serves until they arrive. // Later changes load when the connection changes, so a new deployment needs a reconnect or restart. yield* refresh().pipe(Effect.forkScoped)
I hit this most after waking my laptop: OpenCode starts the provider again before the network (mobile modem + VPN) is up. My deployments are then missing from the picker, and picking the catalog model instead fails because no deployment has that name.
Log right after resume (resource name redacted):
level=WARN message="failed to sync Azure deployments" cause="HttpClientError: Transport error (GET https://<resource>.openai.azure.com/openai/deployments?api-version=2022-12-01) (cause: TypeError: getaddrinfo ETIMEOUT <resource>.openai.azure.com)"
Expected: retry discovery with backoff after a failed sync, so deployments show up once the network is back.
Related: #52237 is the same no-retry problem for MCP servers, #53413 for the GitHub Copilot model sync.
Plugins
No response
OpenCode version
2.1.1 (latest channel, background service). Not yet tested on 2.1.2.
Steps to reproduce
- Use the
azureprovider with an API key and deployments whose names differ from the catalog model IDs. - Disconnect the network and run
opencode service restart(or wake the machine from sleep before the network is up). - The log shows
failed to sync Azure deployments ... getaddrinfo ..., and only catalog Azure models are listed. - Reconnect the network and wait: the deployments don't appear until the provider is restarted (e.g.
opencode service restart).
Screenshot
No response
Operating System
Ubuntu 26.04.1 (Linux 7.0.0-38-generic x64)
Terminal
No response
Activity
opencode-agent commentedon Oct 21, 2026
Thanks for the clear report and the code pointers. I reproduced this on 2.1.1, and it still happens on the latest
dev(4d59657).What I did (the script below does the same):
<resource>.openai.azure.comat a local HTTPS stand-in for Azure'sGET /openai/deployments?api-version=2022-12-01endpoint, with the stand-in stopped at first to act as "network down", and startedopencode servewithAZURE_API_KEY/AZURE_RESOURCE_NAMEset.failed to sync Azure deployments ... Transport error ... Unable to connect, the same as your log.One limit: in my setup the test deployment wasn't matched to a catalog model, so I couldn't check it showing up in the model list. The missing retry itself is clear from the requests.
As you found, the sync error is logged and dropped. Discovery only runs at startup and when the Azure account is switched, so for now
opencode service restartafter the network is back is the workaround.Related reports with the same no-retry pattern:
Reproduction script (repro.sh)
Run it:
bash repro.shargszero commentedon Oct 21, 2026
I'd like to work on this.
I read the code at the two places you linked —
refresh()inpackages/core/src/plugin/provider/azure.ts, wheredeployments(...)is wrapped in anEffect.catchthat logsfailed to sync Azure deploymentsand resolves toundefined, after whichif (!found) returndrops the result, and the two call sites that start discovery (once at plugin start, and onCredential.Event.Switched). I did not find a retry anywhere in that path, which matches your log and your expectation.The plan is to retry the discovery request rather than restructure discovery itself:
deployments(...)call in anEffect.retrywhose schedule is an exponential backoff bounded by a recursion cap, retried only while the failure is transient (transport/DNS/timeout), not on an auth or shape error. The repository already does exactly this shape inpackages/core/src/mcp/client.ts(retrying:Schedule.max([Schedule.exponential, Schedule.recurs])with aisTransientgate), andpackages/core/src/plugin/provider/local.tsis precedent for a provider plugin using a schedule, so I would follow that idiom rather than invent a new one.refresh()with a transport that fails twice and then succeeds, asserting the model list is rebuilt once the third attempt returns deployments, plus one asserting a non-transient failure is not retried.One question before I build it, because it decides the shape. #52237 (MCP) and #53413 (Copilot sync) are the same no-retry problem in other subsystems and both are still open, so a retry that lives only in
azure.tsfixes one of three symptoms. I am happy to do the contained azure fix — that is what this issue asks for — but if you would rather have a shared retry helper that all three call sites use, say so and I will scope it that way instead. I would rather ask now than have you redirect a finished patch.I also cannot reproduce the wake-from-sleep timing on a Linux sandbox, so my verification will be the induced-failure path above rather than your mobile-modem scenario; I will say so in the PR.
argszero commentedon Oct 21, 2026
Update: the fix is implemented and tested, and only my open-PR budget keeps it from being a PR.
Branch
azure-discovery-retry(commit20265c7773) retriesdeployments(...)while the failure can pass — a transport error, a timeout, a 408/429, or a 5xx other than 501 — with a 1, 2, 4, 8, 16, 30 second ladder bounded at 8 attempts, about two minutes. A failure that cannot pass, such as a rejected key, still logsfailed to sync Azure deploymentsonce and stops, as it does today. A switch still interrupts discovery through the existing fiber handle, so a pending retry never answers the old connection.Two tests cover both arms: the transient one fails without the change and passes with it, and the negative one keeps a retried 401 from slipping in later. Nothing else in
packages/coremoves: I ran the suite with and without the patch and the failing test names differ only by that new test.I already have three open PRs, which is the limit here for a contributor without write access, so I will open this one as soon as one of them lands. If you would rather have the shared retry helper covering this together with #52237 and #53413, say so and I will rework it that way before opening — neither of those retries discovery today, so there is no existing shape to extract yet.
argszero commentedon Oct 24, 2026
The fix is now open as #58576 (
c2cb638711, rebased onto currentdev). It closes this issue.What it does:
deployments(...)is retried on a 1, 2, 4, 8, 16, 30 second ladder bounded at 8 attempts, and only a failure that can pass is retried — a transport error, a timeout, a 408, a 429, or a 5xx other than 501. A rejected key still logsfailed to sync Azure deploymentsonce and stops, as it does today, so the warning you saw keeps its meaning. The retry sits before the existingEffect.catchthat turns a failure intoundefined, which is what would otherwise swallow the retried attempt, and a switch still interrupts discovery through the existing fiber handle so a pending retry cannot answer the previous connection.Two tests cover both arms, and the transient one fails on the base (
22 pass, 1 fail) and passes with the change.fix(core): retry Azure discovery when the CLI cannot mint a token
fix(core): give the Azure CLI the same retry window as the request
argszero commentedon Oct 28, 2026
Note on where the fix lives, so it is not lost with the closed pull request.
The retry for Azure deployment discovery is on my fork as branch
azure-discovery-retry, tip183cb8358e. It is no longer an open pull request: accounts without write access here can keep at most three pull requests open at a time, and I closed it to give a different fix its first pickup window. The branch is unchanged and still applies cleanly to today'sdev(b6c43a524265).What it does:
packages/core/src/.../azure.tscalls the resource-manager deployment list once per discovery, so a single transient failure — a timeout, a 5xx, a dropped connection — discards the whole discovery and the provider disappears from the model list until the next poll. The branch adds a small bounded retry around that call, leaves the successful path untouched, and adds a test that fails without the retry and passes with it.If you (or a maintainer) would rather have this as a pull request, say so here and I will re-cut it as soon as a slot is free. If the behaviour has since been fixed upstream another way, tell me and I will drop the branch instead.