Skip to content

feat(server): make the plugin activation budget configurable - #57917

Closed
ggrabarz wants to merge 1 commit into
anomalyco:devfrom
ggrabarz:server/plugin-activation-budget
Closed

ggrabarz wants to merge 1 commit into
anomalyco:devfrom
ggrabarz:server/plugin-activation-budget

Conversation

@ggrabarz

@ggrabarz ggrabarz commented Oct 21, 2026 •

Copy link
Copy Markdown

Problem

A request that needs the model catalog waits for the location's plugins to activate, and gives up after five seconds:

ServiceUnavailableError: Location plugins did not finish activating

Five seconds is a fixed cost that has to cover the whole activation of a cold location — building the location, resolving its plugin sources, loading the plugin modules, and running their setup. On a loaded machine that is not always enough, and the failure mode is worse than a slow response: the request fails while activation is still making progress, and the client then retries against the same location.

Measured on a busy machine, one location was still activating seven seconds after it started warming up, while every request that needed its catalog had already been rejected at five.

Change

The budget is now read from OPENCODE_PLUGIN_ACTIVATION_TIMEOUT, so a slow machine can give its plugins the time they need:

OPENCODE_PLUGIN_ACTIVATION_TIMEOUT="30 seconds" opencode serve
  • The default stays at five seconds, so nothing changes for anyone who does not set it.
  • An unreadable value keeps the default rather than failing every request.
  • The variable is read per request, so a server that inherits it from its parent picks it up without a restart.

packages/web/src/content/docs/cli.mdx documents the variable.

Verification

  • bun test test/location-plugins.test.ts in packages/server: 5 pass, 0 fail
  • Red-green: with the change reverted, the file fails to load because activationBudget does not exist; the behavioural assertions fail with the hard-coded budget in place
  • bun run typecheck in packages/server: clean
  • bunx oxlint on both changed files: 0 warnings, 0 errors
  • bun run check: green

Notes

This does not change what happens when a location's plugins genuinely fail — those still surface as a failed plugin in the inventory, and a request that cannot be served still gets a 503. It only gives slow-but-progressing activation the room it needs, which is the case the fixed five-second wall turned into a failure.

The client side already retries a failed sync, so raising the budget is a way to avoid the retry storm rather than a replacement for it.


Added by the opencode bot (AI-generated).

Related: #57204, #55937

Public API: adds the OPENCODE_PLUGIN_ACTIVATION_TIMEOUT environment variable (backwards compatible, default unchanged at 5s)

A request that needs the model catalog waits for the location's plugins to
activate, and gives up after five seconds with
`ServiceUnavailableError: Location plugins did not finish activating`.

Five seconds is a fixed cost that has to cover the whole activation of a cold
location: building the location, resolving its plugin sources, loading the
plugin modules, and running their setup. On a loaded machine that is not
always enough. When it is not, the request fails even though activation was
still making progress, and the client retries against the same location.

The budget is now read from OPENCODE_PLUGIN_ACTIVATION_TIMEOUT, so a slow
machine can give its plugins the time they need. The default stays at five
seconds, so nothing changes for anyone who does not set it, and an
unreadable value keeps the default rather than failing every request.

The environment variable is read per request, so a server that inherits it
from its parent picks it up without a restart.
@github-actions github-actions Bot added the needs:compliance This means the issue will auto-close after 2 hours. label Oct 21, 2026
@github-actions

Copy link
Copy Markdown
Contributor

This PR doesn't fully meet our contributing guidelines and PR template.

What needs to be fixed:

  • PR description is missing required template sections. Please use the PR template.

Please edit this PR description to address the above within 72 hours, or it will be automatically closed.

If you believe this was flagged incorrectly, please let a maintainer know.

@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.

The variable works as described when calling awaitPlugins directly: with it set, slow activation either gets more time or fails sooner. I checked this with the added tests (5 pass), a clean bun run typecheck in packages/server, and a probe of how activationBudget() parses a range of values. I did not run a full server against a slow plugin.

Public API: adds the environment variable OPENCODE_PLUGIN_ACTIVATION_TIMEOUT and documents it in the CLI docs. It is backwards compatible because the default stays at 5 seconds. No change to the HTTP API, the SDK or config.

Concerns:

  • Wrong formats are silently ignored. 30000, 30 and 30s all quietly fall back to 5s, so a user who sets the variable can easily get no effect with no feedback. The docs row doesn't say which format is expected.
  • Zero and negative values are accepted. 0 millis and -5 seconds make every request that waits for plugins fail immediately with a 503.

Details are in the inline comments. Small point: the description says reading the variable per request means a running server picks up a new value without a restart. A process's environment doesn't change after it starts, so that benefit doesn't exist. It doesn't block anything, but the per-request read only helps the tests.

Related: #57204, #55937

const configured = process.env.OPENCODE_PLUGIN_ACTIVATION_TIMEOUT?.trim()
if (!configured) return defaultActivationBudget
try {
return Duration.fromInputUnsafe(configured as Duration.Input)

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.

fromInputUnsafe on a raw string, with a catch that hides failures, causes two problems:

  • Wrong formats do nothing, silently. Common forms like 30000, 30 and 30s (and 1e9 seconds) all fall back to 5s, so a user who sets the variable gets no change and no warning.
  • Zero and negative values are accepted. 0 millis gives 0 and -5 seconds gives -5s, which turns every awaitPlugins call into an immediate 503.

A plain positive number of milliseconds, like the existing OPENCODE_EXPERIMENTAL_BASH_DEFAULT_TIMEOUT_MS, would be harder to get wrong. Whatever format you keep, please reject values that aren't positive, and log a warning when a value is ignored instead of hiding it.

| `OPENCODE_TUI_CONFIG` | string | Path to TUI config file |
| `OPENCODE_CONFIG_DIR` | string | Path to config directory |
| `OPENCODE_CONFIG_CONTENT` | string | Inline json config content |
| `OPENCODE_PLUGIN_ACTIVATION_TIMEOUT` | string | How long location plugins may take to activate |

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.

Please state the accepted format and the default here, for example "Plugin activation timeout, e.g. 30 seconds (default 5 seconds)". Without it, users will likely try 30000 or 30s, and both are currently ignored.

@github-actions

Copy link
Copy Markdown
Contributor

This pull request has been automatically closed because it was not updated to meet our contributing guidelines within the 72-hour window.

Feel free to open a new pull request that follows our guidelines.

@github-actions github-actions Bot removed the needs:compliance This means the issue will auto-close after 2 hours. label Oct 24, 2026
@github-actions github-actions Bot closed this Oct 24, 2026
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.

1 participant