Rename-provider WorkspaceEdit loses destination casing through asCanonicalUri on Windows
- Dominant language
- TypeScript
- Stars
- 193k
- Forks
- 42.4k
- PR merge metrics
- PR metrics pending
Description
Does this issue occur when all extensions are disabled?: Not tested without extensions; the reproduction uses the Python/Pylance rename provider. Debugger observations below isolate a transformation in VS Code after it receives the provider's edit.
- VS Code Version: 1.137.0
- OS Version: Windows 11 Enterprise, build 26200; filesystem case sensitivity disabled
- Extensions: Python 2026.4.0, Pylance 2026.3.1 for the original symptom reproduction; also traced with a Pylance development build
- Latest Insiders: not separately tested
Related user report: https://github.com/microsoft/pylance-release/issues/8201
## Summary
A file-rename destination returned by a rename provider has the requested lowercase spelling, but VS Code's workspace-edit revival calls `asCanonicalUri` on `newResource` and replaces it with a previously cached uppercase URI. This can collapse a case-only rename to a no-op or restore an old filename's casing after an intermediate rename. Text edits retain the requested lowercase module name, leaving imports and the actual directory entry inconsistent.
## Steps to reproduce
On Windows with Python and Pylance 2026.3.1 enabled:
1. Open a folder containing `scenarios/main.py`:
```python
class Something: ...
x = Something()
```
2. On `Something` in the class declaration, run **Move symbol to new file**. This creates `scenarios/Something.py` and changes the importer to `from scenarios.Something import Something`.
3. In that import, put the cursor on the module component `Something` (not the imported class) and press F2. Rename it to `something`. The direct case-only rename is a no-op in this build.
4. Without reloading the window, use F2 on the same module component to rename it to `_something`. Confirm the disk file is `_something.py` and the import uses `scenarios._something`.
5. Use F2 again to rename `_something` to `something`.
6. Inspect the actual filename with directory enumeration, not only the Explorer label.
Expected: `scenarios/something.py` on disk and `from scenarios.something import Something` in the importer.
Actual: the file is named `scenarios/Something.py`, while the import is lowercase. Pylance reports the import as unresolved.
The fresh move, failed direct rename, and intermediate rename are intentional: the failure depends on previously remembered URI casing. A fresh independent fixture can behave differently.
## Debugger evidence
The following values were observed while performing the editor actions in a real VS Code session. Paths are sanitized; these are payload excerpts, not a standalone extension reproduction.
For the final `_something` -> `something` rename, the server returned a text edit using `scenarios.something` and this resource operation:
```json
{
"kind": "rename",
"oldUri": "file:///c%3A/repro/scenarios/_something.py",
"newUri": "file:///c%3A/repro/scenarios/something.py"
}
```
The renderer callback registered by `$registerRenameSupport` received the lowercase destination from `$provideRenameEdits`. Stepping through workspace-edit revival showed:
1. Incoming `newResource`: `file:///c%3A/repro/scenarios/something.py`.
2. `asCanonicalUri` finds an existing URI under the case-insensitive comparison key whose path spelling is `Something.py`.
3. The returned canonical URI replaces `newResource` with `file:///c%3A/repro/scenarios/Something.py`.
4. The text edit remains lowercase. The resulting directory entry follows the altered uppercase destination.
A direct case-only probe also showed a valid `Something.py` -> `something.py` resource operation arriving in the renderer, then becoming identical uppercase old/new resource URIs after canonicalization.
This is not a request to make resource identity globally case-sensitive. A rename destination carries the spelling requested for the new directory entry, which should not be replaced with a previously remembered spelling during edit revival.
Pylance also had separate import-edit suppression problems for case-only renames. Those do not explain the final intermediate-rename failure above: both the text edit and resource destination were correct when they reached VS Code. Correcting the provider's direct-rename text edits likewise does not prevent the client from retaining uppercase disk spelling.
Contributor guide
Assessment
This issue has not been assessed yet.