Skip to content

Conversation

@dmealing
Copy link

@dmealing dmealing commented Jan 30, 2026

Summary

  • The config handler calls fetchAvailableModels(ctx.client) during OpenCode bootstrap, before the server is ready to handle requests
  • On fresh installs with no cache files, this triggers client.provider.list() which sends an HTTP request back to the OpenCode server
  • The server is blocked waiting for the config handler to return → circular dependency / deadlock
  • The TUI/web UI never renders (black screen), and Ctrl+C can also hang because cleanup calls hit the same deadlocked server

Root cause

This is a regression from 80ee52f which re-introduced client API calls as a fallback in fetchAvailableModels, undoing the fix in ab3e622 ("fix: use cache file for model availability instead of SDK calls") that explicitly made the client parameter unused (_client) to prevent this exact deadlock.

Why it appears platform-specific

The issue was reported as Linux-only, but it's actually a fresh-install problem:

  1. The connected-providers.json cache is only written by the session.created event handler
  2. On a fresh install, no cache exists → readConnectedProvidersCache() returns nullconnectedProvidersUnknown = true → client API call → deadlock
  3. Since the session never starts, the cache is never written — the bug is permanent once hit
  4. Users with existing cache files (e.g. from before the regression in 80ee52f) skip the client call path entirely and never encounter the issue

Changes

  • config-handler.ts: Pass undefined instead of ctx.client to createBuiltinAgents() and fetchAvailableModels() during the config phase, forcing cache-only model resolution (same approach as ab3e622)
  • manager.ts: Add 3-second force-exit timeout to SIGINT handler so Ctrl+C always terminates even when cleanup API calls hang on a deadlocked server

Test plan

  • Verified fix on Linux (Pop!_OS 22.04, OpenCode v1.1.44, oh-my-opencode v3.1.9)
  • Confirmed ~/.cache/oh-my-opencode/connected-providers.json does not exist on fresh install
  • Pre-fix log shows /provider request stuck at "started" indefinitely (deadlock)
  • Post-fix log shows all server requests completing, plugins loading, MCP servers connecting
  • Verify no regression on macOS (should be no-op since cache files exist)

🤖 Generated with Claude Code


Summary by cubic

Prevents a startup deadlock on fresh installs by avoiding client API calls during config bootstrap. Adds a 3s force-exit on SIGINT so Ctrl+C still works if cleanup hangs.

  • Bug Fixes
    • Config handler now uses cache-only model resolution by passing undefined client to createBuiltinAgents and fetchAvailableModels, avoiding circular server calls before the server is ready.
    • Background agent adds a 3s force-exit timer in the SIGINT handler to ensure the process exits even if cleanup API calls hang.

Written for commit b21e42d. Summary will update on new commits.

The config handler is called by OpenCode during bootstrap, before the
server is ready to handle requests. When fetchAvailableModels() is
passed the client object and no cache exists (fresh install), it calls
client.provider.list() which sends an HTTP request back to the OpenCode
server. The server is blocked waiting for the config handler to return,
creating a circular dependency that hangs the entire startup.

This is a regression from 80ee52f which re-introduced client API calls
as a fallback in fetchAvailableModels, undoing the fix in ab3e622 that
explicitly made the client parameter unused to prevent this exact
deadlock.

The issue appears platform-specific (reported on Linux) but is actually
a fresh-install problem: the connected-providers cache is only written
by the session.created event handler, which never fires when the
deadlock prevents startup. Users with existing cache files (e.g. the
author on macOS) skip the client call path entirely.

Changes:
- config-handler.ts: Pass undefined instead of ctx.client to
  createBuiltinAgents() and fetchAvailableModels() during the config
  phase, forcing cache-only model resolution
- manager.ts: Add 3-second force-exit timeout to SIGINT handler so
  Ctrl+C always works even when cleanup hangs on a deadlocked server

Co-Authored-By: Claude Opus 4.5 <[email protected]>
@github-actions
Copy link
Contributor

Thank you for your contribution! Before we can merge this PR, we need you to sign our Contributor License Agreement (CLA).

To sign the CLA, please comment on this PR with:

I have read the CLA Document and I hereby sign the CLA

This is a one-time requirement. Once signed, all your future contributions will be automatically accepted.


I have read the CLA Document and I hereby sign the CLA


You can retrigger this bot by commenting recheck in this Pull Request. Posted by the CLA Assistant Lite bot.

Copy link

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

No issues found across 2 files

Confidence score: 5/5

  • Automated review surfaced no issues in the provided summaries.
  • No files require special attention.

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