microsoft / microsoft/vscode

Multi-diff model teardown re-enters binding disposal and throws

Open
#335,720 0 comments 0 reactions 1 assignee Claimed by @hediet View on GitHub
bug multi-diff-editor
Dominant language
TypeScript
Stars
193k
Forks
42.4k
PR merge metrics
PR metrics pending

Description

Does this issue occur when all extensions are disabled?: N/A — reproduced in isolated browser diagnostics with no extension host; not separately reproduced via the installed app's UI.

- VS Code Version: 1.138.0-insider, `7b7e49c83affacfac726040280da69b4999f3e01` (2026-09-11). Diagnostics used `33a6a088236067e1a16d08481d617dfcf6da2e2c`; affected code is identical.
- OS Version: macOS 26.6.2, arm64; headless Chromium diagnostics.

Steps to Reproduce:

1. In a browser harness, create a real compact `MultiDiffEditorWidget` at 900×640 with 90 two-sided text diffs, real model services, and a synthetic text-model resolver. Collapse all before attaching the view model with `preserveFocus: true`.
2. Collapse/expand five times, allowing two animation frames between operations, then call `widget.setViewModel(undefined)` and dispose the input.

**Observed:** teardown reports `Cannot unbind a diff editor template from a different item` (11 occurrences in the diagnostic run). This was found during investigation; the original Insiders renderer log did not contain this error.

**Cause:** [`ManagedVirtualizedItem._clearBinding`](https://github.com/microsoft/vscode/blob/7b7e49c83affacfac726040280da69b4999f3e01/src/vs/editor/browser/widget/multiDiffEditor/virtualizedItemManager.ts#L225-L247) leaves its template reference/current binding exposed while disposing the binding. The captured synchronous chain is:

```text
_clearBinding -> DiffEditorItemBinding.dispose -> template.unbind
-> setItem(undefined) -> editor.setDiffModel(null)
-> CodeEditorWidget.onEndUpdate -> observable reaction
-> _clearBinding -> same binding.dispose -> template.unbind throws
```

The inner call sees an already-cleared template view model, but binding disposal is not yet complete; the binding's `isDisposed` guard cannot prevent re-entry.

**Expected / fix direction:** release each binding and return its template exactly once, without teardown errors. Make release re-entrancy-safe through an explicit releasing state or atomic lifecycle transition, preserving cleanup ordering rather than suppressing the assertion. A temporary `_clearBinding` re-entrancy guard eliminated all 11 errors.

Add regression coverage using the real model-detachment/observable callback path; a passive mock binding misses the re-entry. This is separate from #206959: the listener-count warning remains after this lifecycle problem is removed.

Relevant lifecycle history: https://github.com/microsoft/vscode/commit/10de1b9343662a4043258b418630481e27c863b1 (@hediet).

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.