Skip to content

fix(opencode): skip falsy plugin hook returns instead of crashing provider listing#36102

Open
CryptArchy wants to merge 3 commits into
anomalyco:devfrom
CryptArchy:guard-falsy-plugin-hooks
Open

fix(opencode): skip falsy plugin hook returns instead of crashing provider listing#36102
CryptArchy wants to merge 3 commits into
anomalyco:devfrom
CryptArchy:guard-falsy-plugin-hooks

Conversation

@CryptArchy

@CryptArchy CryptArchy commented Jul 9, 2026

Copy link
Copy Markdown

Issue for this PR

Closes #35772

Type of change

  • Bug fix
  • New feature
  • Refactor / code improvement
  • Documentation

What does this PR do?

A plugin entry that resolves to no hooks gets pushed into the hooks array as-is in applyPlugin. The Provider state build later does const p = hook.provider (provider/provider.ts:1372) one line before its null-guard, so a single undefined entry 500s GET /config/providers and takes opencode attach and the web UI down with it — the error in #35772.

The easiest way to hit it: a stray helper export on a legacy-shaped plugin module. Every function export is loaded as a plugin, so the helper is called and its undefined return lands in the hooks array. That's how I hit it with my own plugin (identical on 1.16.2 and 1.17.x). A v1 module whose server() resolves undefined does the same. It also explains "happens with plugins removed": file-based plugins are auto-discovered from {plugin,plugins}/*.{ts,js} in every config dir, so a leftover file counts even with an empty plugin array.

Fix: skip falsy hook returns on the v1, legacy, and internal-plugin paths, and log a warning naming the plugin spec. I kept the guard at the source instead of adding ?. at the consumers so the offending plugin gets named instead of silently ignored.

How did you verify your code works?

  • New packages/opencode/test/plugin/falsy-hooks.test.ts covers both module shapes; it fails 2/2 without the src change and passes with it. bun test test/plugin/ and test/provider/provider.test.ts are green; tsgo --noEmit and prettier clean.
  • Reproduced the crash end-to-end against a released opencode serve with a plugin carrying a stray helper export (/config/providers → 500 on every poll); removing the export restores 200. The loader guard itself is covered by the new tests.

Screenshots / recordings

n/a — no UI change.

Checklist

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

A plugin entry that resolves to no hooks (a stray helper export on a legacy
module, or a v1 server() resolving undefined) was pushed into the hooks array
unchecked. Consumers dereference entries directly — hook.provider in the
Provider state build — so one undefined entry 500s /config/providers and
makes the whole instance unusable (attach and web UI both die).

Skip falsy returns with a warning naming the offending plugin spec, guard the
internal-plugin push the same way, and pin the behavior with regression tests
for both module shapes.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@github-actions github-actions Bot added needs:compliance This means the issue will auto-close after 2 hours. needs:title labels Jul 9, 2026
@github-actions

github-actions Bot commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Hey! Your PR title plugin: skip falsy hook returns instead of crashing provider state doesn't follow conventional commit format.

Please update it to start with one of:

  • feat: or feat(scope): new feature
  • fix: or fix(scope): bug fix
  • docs: or docs(scope): documentation changes
  • chore: or chore(scope): maintenance tasks
  • refactor: or refactor(scope): code refactoring
  • test: or test(scope): adding or updating tests

Where scope is the package name (e.g., app, desktop, opencode).

See CONTRIBUTING.md for details.

@github-actions

github-actions Bot commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

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

Related PR Found

PR #35489: fix(plugin): skip non-function exports instead of throwing

Why it's related: This PR addresses a similar issue in plugin loading - skipping invalid plugin exports rather than crashing. While PR #36102 specifically targets falsy hook returns after evaluation, PR #35489 likely handles related plugin validation and export filtering at an earlier stage. Both are part of making the plugin system more resilient to bad plugin exports and helper functions mixed into plugin modules.

@CryptArchy CryptArchy changed the title plugin: skip falsy hook returns instead of crashing provider state fix(opencode): skip falsy plugin hook returns instead of crashing provider listing Jul 9, 2026
@github-actions github-actions Bot removed needs:title needs:compliance This means the issue will auto-close after 2 hours. labels Jul 9, 2026
@github-actions

github-actions Bot commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Thanks for updating your PR! It now meets our contributing guidelines. 👍

CryptArchy added a commit to hellogravel/opencode-lms that referenced this pull request Jul 10, 2026
…module

TTL: every chat completion carries LM Studio's idle ttl (default 3600s,
0 = resident) via providerOptions.openaiCompatible — LMS's REST load/chat
endpoints reject the key; the OpenAI-compat path accepts it and resets the
idle countdown per request, so eviction is delegated to LMS with no
client-side unload loop.

Context cap: models load at min(contextLength, max_context_length), default
8192, overridable globally and per-model — the VRAM knob, distinct from the
UI-facing limit.context. Discovery maps limit.context from the smallest
loaded instance (or max for cold models), limit.output as
min(context/4, 8192); tools are always advertised (trained_for_tool_use is
diagnostic-only now). @opencode-ai/plugin exact-pinned to 1.17.15.

Loader safety (the 0.3.0-dev incident): a helper export on the entry module
broke OpenCode provider listing — the legacy loader calls every function
export as a plugin and pushes undefined returns into its hooks array
unchecked, killing /config/providers (500) and with it attach + web UI,
identically on 1.16.2 and 1.17.x. applyCompletionTtl now lives in
src/ttl.ts, the default export is the v1 PluginModule shape ({ id, server })
so named exports are ignored, and two guards pin the contract:
tests/plugin-exports.test.ts and docker/smoke.sh (boots the harness against
latest OpenCode and asserts /config/providers lists lmstudio — doubles as
the version-bump check; needs a reachable LM Studio). README documents
OpenCode ≥1.16.2 (validated on 1.17.15; the plugin pin is types-only).
Upstream hardening: anomalyco/opencode#36102.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
CreatorGhost added a commit to CreatorGhost/TheCode that referenced this pull request Jul 12, 2026
CreatorGhost added a commit to CreatorGhost/TheCode that referenced this pull request Jul 12, 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.

[Bug] Desktop v1.17.14 Provider.list() TypeError crash - UI shows no models/providers on every startup

1 participant