Skip to content

dispose() rejects with "row <id> is disabled" and skips the rest of teardown #38

Open

Description

@iceteaSA

What happens

With OAuth account rows disabled (enabled: false) in the account store, every OpenCode exit logs:

level=ERROR message="plugin dispose hook failed" error="row <id> is disabled"

Observed on every exit (5 times in one day) with @cortexkit/opencode-antigravity-auth 3.0.4, matching upstream main at 48919aa. Both configured OAuth rows were disabled.

Where it comes from

  • PluginResult.dispose calls lifecycle.dispose() (packages/opencode/src/plugin/index.ts:956-959), which first awaits disposeAccountRuntime() (packages/opencode/src/plugin/lifecycle.ts:225).
  • disposeAccountRuntime() (lifecycle.ts:133-163) has two awaits that can reject:
    • await liveQueueDrained at :157. The void liveQueueDrained.catch(() => {}) at :152 silences the unhandled-rejection warning, but the later await still throws.
    • if (failed) throw failed.reason at :163 rethrows the first retired runtime that rejected.
  • The store's enabled-row guard throws row-disabled (packages/core/src/common-auth-embedded/store/refresh.js:20-21). CoreAccountManager.refreshAccount() (packages/core/src/account-manager.ts:1248-1257) calls repository.refresh() without checking enabled. The proactive refresh queue skips disabled accounts (packages/opencode/src/plugin/refresh-queue.ts:179-183), but only when it selects accounts on a tick.

I haven't pinned down which in-flight operation is refreshing the disabled row at exit.

Impact

This skips more than logging. If disposeAccountRuntime() throws, the rest of dispose() (lifecycle.ts:226-246) doesn't run: disk signature cache shutdown, producer disposal, the sidebar write drain, and consumer disposal (RPC server, file logger) are skipped.

Repro

  1. Disable the OAuth account rows in the account store (enabled: false).
  2. Start OpenCode, run a session, then exit.
  3. The log shows the ERROR line above.

Expected

A disabled row during teardown has nothing to refresh. dispose() should complete and run every teardown step. Either of these fixes would work:

  • Check enabled in refreshAccount() (or before scheduling a refresh) so a disabled row is never refreshed.
  • Settle the drains in disposeAccountRuntime() (Promise.allSettled, log the failures) so one failed refresh can't abort teardown.

For comparison, openai-auth skips disabled accounts before its quota refresh (packages/core/src/refresh-all-quota.ts:323-332 in cortexkit/openai-auth).

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions