The method changed trust before its promise described completion.
Visual Studio Code’s Workspace Trust service coordinates a state transition with registered asynchronous participants and emits an onDidChangeTrust event when the transition has completed. Callers can await setWorkspaceTrust() or the underlying setUrisTrust() API to sequence later work after that lifecycle.
Issue #328625 identified that the returned promise could settle too early. PR #328626, authored by Zain Nadeem, restored the completion contract and added a deterministic regression test using a deliberately paused transition participant.
Awaiting the public API did not await its asynchronous participants.
A caller could execute await setWorkspaceTrust(true) and resume while a transition participant was still running. At that point, the internal trusted value had changed, but participant work had not completed and onDidChangeTrust had not yet fired.
state changes → caller resumes → participant completes → eventstate changes → participant completes → event → caller resumesThis is a lifecycle consistency problem rather than a claim that the trust decision itself was bypassed. The upstream PR does not classify the bug as a vulnerability.
An async method invoked a promise-returning operation without awaiting it.
setUrisTrust() awaited canonicalization of its URI list, then called doSetUrisTrust(...) without awaiting or returning that operation’s promise. Because the outer function was itself async, it resolved after starting the transition rather than after the transition chain finished.
async setUrisTrust(uris, trusted) {
this.doSetUrisTrust(await canonicalize(uris), trusted);
// The returned promise is discarded.
}The defect was only one missing keyword, but the semantic difference covered participant execution, event ordering, and every caller relying on the method’s completion.
Propagate completion through the existing promise chain.
The core change adds await to the doSetUrisTrust() call. No new transition mechanism is introduced; the public method now reflects the lifecycle already implemented by the lower-level operation.
async setUrisTrust(uris, trusted) {
await this.doSetUrisTrust(await canonicalize(uris), trusted);
}The PR also adjusts Workspace Trust editor deletion focus handling so the UI selects a stable neighboring row before awaiting the trust mutation, then fires the delete event after the asynchronous operation completes.
Control the participant instead of relying on timing.
The regression registers a Workspace Trust transition participant whose completion is gated by a manually released promise. It also records whether the participant started, whether it completed, whether the trust-change event fired, and whether the public promise resolved.
| Phase | Expected state |
|---|---|
| Before transition | No participant, event, or resolution |
| Participant paused | Trust changed; public promise unresolved; event not fired |
| Participant released | Participant completes and event fires |
| Public await completes | Full transition is finished |
This structure avoids a sleep-based race. The test advances only when each observable phase is established. The PR record documents compilation, the focused regression, the broader Workspace Trust test selection, and whitespace validation.
The completion contract and UI sequencing were merged together.
Zain authored PR #328626. The contribution changed the trust service, the Workspace Trust editor’s deletion flow, and the service tests. It was merged on 19 August 2026 as commit 773e6102d24184f4f9eaee9482d25cb85a6d6514.
The article deliberately does not claim release inclusion or broader security impact because neither is needed to describe the verified upstream change.
An async API's promise is part of its public state model.
- Calling an asynchronous operation from an
asyncmethod does not propagate completion unless its promise is awaited or returned. - Lifecycle tests should assert event ordering and participant completion, not merely the final boolean state.
- Controllable promises produce deterministic concurrency tests without arbitrary delays.
- UI state changes around asynchronous mutations should select and restore focus deliberately.