Skip to content

SchemaStore.dispose() writes to an already-disposed output channel: "Channel has been closed" on every shutdown #885

Description

Reproduced against the CI-built vscode-documentdb-0.10.0.vsix from #883 (artifact of run 31513450303).

Summary

Every extension-host shutdown logs an unhandled error while disposing the extension's subscriptions. SchemaStore.dispose() calls logStats(), which writes to the shared output channel — but that channel has already been disposed by then, so the write throws.

[error] An error occurred when disposing the subscriptions for extension 'ms-azuretools.vscode-documentdb':
[error] Error: Channel has been closed
    at AzExtLogOutputChannel.appendLine (main.js:2:362407)
    at AzExtLogOutputChannel.appendLog  (main.js:2:362726)
    at SchemaStore.logStats            (main.js:2:2120687)
    at SchemaStore.dispose             (main.js:2:2123726)

Observed in 4 of 6 windows that reached a clean shutdown. It is the only extension-level error in any of those runs — everything else is clean.

Cause

The ordering is deterministic, not a race. activate() registers the output channel into context.subscriptions before it registers SchemaStore:

ext.outputChannel            -> subscriptions[i]
…
SchemaStore.getInstance()    -> subscriptions[j]     where j > i

VS Code disposes context.subscriptions in registration order, so the channel at i is closed before SchemaStore at j runs its own dispose(). logStats() then writes to a closed channel.

Worth noting the ext.outputChannel?. optional chaining in that path reads as a guard but isn't one — the object still exists, it is merely closed, so ?. passes and appendLine throws.

Steps to reproduce

  1. Install vscode-documentdb-0.10.0.vsix.
  2. Open a window and let the extension activate.
  3. Close the window.
  4. Check logs/<session>/window1/exthost/exthost.log.

Expected: shutdown completes without errors.
Actual: the stack above, on most shutdowns.

Impact

Not user-visible — it is log noise at shutdown, and no functionality is lost. Two reasons it is still worth fixing:

  • It fires on every window close, reload, and extension update, so it is persistent noise in any log a user attaches to a bug report.
  • VS Code aborts the remainder of that subscription-disposal pass when a disposable throws, so a genuine cleanup failure registered after SchemaStore could be masked by this one.

Suggested fix

Any of:

  • Register SchemaStore before ext.outputChannel so it disposes first; or
  • Have logStats() bail when the channel is closed (track a disposed flag rather than relying on ?.); or
  • Move the stats logging out of dispose() entirely — emitting telemetry at shutdown is fine, but writing to a channel that is itself a subscription is inherently order-dependent.

The first is the smallest change; the second is the most robust if anything else ever logs during disposal.

Suggested milestone

0.10.1 — not a release blocker for 0.10.0 unless the reorder turns out to be a confident one-liner.

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