Skip to content

fix(server): await plugin activation in plugin list/check/update - #56998

Open
potoior wants to merge 3 commits into
anomalyco:devfrom
potoior:plugin-activation
Open

potoior wants to merge 3 commits into
anomalyco:devfrom
potoior:plugin-activation

Conversation

@potoior

@potoior potoior commented Oct 19, 2026 •

Copy link
Copy Markdown

Issue for this PR

Closes #54654

Type of change

  • Bug fix

What does this PR do?

plugin.list read the plugin registry without waiting for plugin activation, so the first call on a cold location returned an empty list while configured plugins were still registering (opencode plugin list → "No plugins found", exit 0; the second call lists everything). Same cold-location race already fixed for mcp.list, skill.list, agent.list, and model.list.

  • plugin.list, plugin.check, and plugin.update now yield* awaitPlugins before reading the inventory, matching the model.list pattern.
  • Declared ServiceUnavailableError on plugin.list and plugin.check in the protocol (the await can fail if activation times out); plugin.update already declared it.
  • Client + openapi.json regenerated (adds 503 to the two endpoints' declared statuses).

This is a re-open of #54676, which was auto-closed for a non-compliant PR description — code is unchanged apart from the rebase onto current dev.

How did you verify your code works?

  • Regression test packages/server/test/plugin-endpoint.test.ts uses a fixture plugin that only becomes available after its async setup completes: without the await, plugin.list returns [] and the test fails; with it, the plugin is listed. Passes 1/1.
  • bun typecheck clean in packages/protocol, packages/server, packages/client.
  • bun run generate in packages/client produces no further diff.

Screenshots / recordings

N/A (no UI change)

Checklist

  • I have tested my changes locally
  • I have not included unrelated changes in this PR

Added by the opencode bot (AI-generated).

Related: #51282, #51680, #54192, #53724

Public API: plugin.list and plugin.check now declare a 503 ServiceUnavailableError (backwards compatible)

plugin.list read the registry without waiting for plugin activation, so a
cold-location call could return an empty list while the configured plugins
were still registering (same race already fixed for mcp.list, skill.list,
agent.list and model.list). Await plugin activation before listing, matching
model.list, and add the endpoint's ServiceUnavailableError to the protocol
contract (regenerating the client).

A regression test registers a local plugin that becomes available only after
setup completes; without the await, plugin.list returns [] and the test fails.
Keeps these endpoints consistent with plugin.list so a cold location can't
fail with a false 'not in the current server inventory'. Declares
ServiceUnavailableError on plugin.check and regenerates the client/openapi.
@github-actions

Copy link
Copy Markdown
Contributor

The following comment was made by an LLM, it may be inaccurate:

@opencode-agent opencode-agent Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This fixes #54654. On the base, the reproduction script printed "No plugins found" on the first plugin list in every new directory. With this PR the plugin is listed on the first call. The new test fails on the base and passes here, and typechecks pass for server, protocol and client.

Public API: GET /api/plugin (plugin.list) and POST /api/plugin/check (plugin.check) now declare a 503 ServiceUnavailableError in the protocol, openapi.json and the generated client. Status lists change from [400, 401, 404] to [400, 401, 404, 503]. This is backwards compatible: nothing is removed, but clients may now get a 503 from these endpoints.

Design question for maintainers: commit 8905af5 ("refactor(protocol): remove plugin activation wait") deliberately removed the activation waits from plugin.check and plugin.update. Its audit note says "Activation timing is an internal server concern. Catalog reads remain non-blocking." This PR brings the waits back and adds one to plugin.list, so a maintainer should confirm this is wanted. The description also says the same race is already fixed this way for mcp.list, skill.list and agent.list, but none of those handlers wait for activation on dev (see #51282, #51680, #54192). There is also a possible regression, noted inline: a 503 when activation takes longer than 5 seconds.

Related: #51282, #51680, #54192, #53724

"plugin.list",
() =>
Effect.gen(function* () {
yield* awaitPlugins

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

awaitPlugins waits for the whole activation, including the second pass that installs missing package plugins. If it takes more than 5 seconds it fails with a 503 ("Location plugins did not finish activating"). Before this change, plugin list in a new directory with a slow or stuck npm install still showed the local plugins and any failures. Now it errors, and this is the command people use to diagnose plugin problems. Could plugin.list fall back to the current inventory on timeout instead of failing? check and update have the same issue: they would 503 instead of acting on the plugins already loaded.

WhollyMindless pushed a commit to WhollyMindless/opencode-self-improvement that referenced this pull request Oct 27, 2026
…nfigured' race

opencode plugin list/check/update against a not-yet-activated background
service reads an empty plugin inventory (anomalyco/opencode#54654, fix
pending in anomalyco/opencode#56998), so the first call after a cold
start can report a configured plugin as missing. Re-running succeeds.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

cli: opencode plugin list prints "No plugins found" on the first call per cold location

1 participant