Skip to content

[WSLC] Encapsulate backend errors in a typed enum for the Rust SDK - #1045

Merged
Soham Das (SohamDas2021) merged 2 commits into
mainfrom
user/sodas/794-wslc-typed-errors
Aug 26, 2026
Merged

[WSLC] Encapsulate backend errors in a typed enum for the Rust SDK#1045
Soham Das (SohamDas2021) merged 2 commits into
mainfrom
user/sodas/794-wslc-typed-errors

Conversation

@SohamDas2021

@SohamDas2021Soham Das (SohamDas2021) commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

📖 Description


Summary

WSLc built every failure as a free-text ScriptResponse via ScriptResponse::error, which leaves failure_phase at its None default. Since mxc_engine::dispatch::map_spawn_error discriminates on exactly that field, all 54 WSLc error sites reached the Rust SDK as an opaque backend_error — a caller could only tell a missing WSL host from a rejected policy by parsing the message.

This adds wslc_common::error::WslcError, mirroring the existing windows_sandbox_lifecycle::error::OneShotError pattern, and routes the WSLc error sites through it:

VariantPhaseSDK code
UnavailableBackendUnavailablebackend_unavailable
RejectedRejectedpolicy_validation
Sdk / HostLaunchFailedbackend_error
RuntimePostLaunchFailedbackend_error

map_spawn_error gains the Rejected → policy_validation arm. No new ErrorCode variants, so the closed cross-surface set (Rust → engine → C ABI → C#) is untouched and the parity gate is unaffected.

No user-visible message changes.Display reproduces each message verbatim; a unit test transcribes main's old sdk_error body as an oracle and asserts byte-identical rendering. The only behavioral change is that failure_phase now carries a real value instead of None — nothing in tests/, sdk/, or scripts/ asserts on it, and the only production readers are map_spawn_error and telemetry's classify_failure (total over the enum).

Validation

  • cargo fmt --all -- --check, cargo clippy -p wslc_common -p mxc_engine --all-targets -- -D warnings — clean
  • cargo test --workspace — 3,912 passed / 0 failed
  • wslc_common with link-wslcsdk — 188/188, including 4 new error::tests
  • Live WSLc E2E on a WSL2 host (run_wslc_all_tests.ps1): 54/57 state-aware + 24/25 one-shot — byte-identical to a pristine main baseline built and run on the same host. All 110 PASS/FAIL lines match exactly.

The 3 E2E failures are pre-existing on main and are not caused by this PR: they come from the WSLc network.proxy gate in config_parser.rs (a file this PR does not touch), fail during config parsing before any WSLc dispatch, and are fixed by #1042. Once #1042 lands and this rebases onto it, the suite is 57/57 + 25/25.

Closes#794

📋 Issue Type

  • Bug fix
  • Feature
  • Task

GitHub Actions runs the PR validation build automatically. The ADO pipeline
(MXC-PR-Build) is the Azure version of the PR pipeline, kept in parity with the GitHub
Actions build; it runs on merge to main, and Microsoft reviewers with write access can trigger it
on a PR with /azp run. See docs/pull-requests.md.

If the dependency-feed-check check fails on a new dependency, the crate must be added to
the feed before the PR can pass. See docs/pull-requests.md
for the steps.

Microsoft Reviewers: Open in CodeFlow

CopilotAI balanced review requested due to automatic review settings August 25, 2026 21:52
@SohamDas2021
Soham Das (SohamDas2021) requested a review from a team as a code ownerAugust 25, 2026 21:52
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
There may be pipelines that require an authorized user to comment /azp run to run.

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds typed WSLc failure classification so Rust SDK callers can distinguish unavailable backends and rejected policies.

Changes:

  • Introduces WslcError with lifecycle-phase mapping.
  • Migrates WSLc error construction while preserving messages.
  • Maps rejected spawn failures to policy validation errors.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 3 comments.

Show a summary per file
FileDescription
src/core/mxc_engine/src/dispatch.rsMaps rejected failures to PolicyValidation.
src/backends/wslc/common/src/error.rsDefines typed WSLc errors.
src/backends/wslc/common/src/lib.rsExposes the error module.
src/backends/wslc/common/src/container_steps.rsConverts shared helper failures.
src/backends/wslc/common/src/wsl_container_runner.rsConverts one-shot runner failures.
Suppressed comments (1)

src/backends/wslc/common/src/error.rs:67

  • Sdk and Host do not always occur during launch. wait_for_process calls sdk_error for WslcGetProcessExitEvent/WslcGetProcessExitCode after the container process is live, and StartedContainer::wait_for_exit maps a COM-entry failure to Host; those failures are therefore incorrectly reported as LaunchFailed instead of PostLaunchFailed. Split launch-time and runtime SDK/host errors, or carry the phase at construction.
 // The SDK call or host bring-up failed; generally worth retrying.
WslcError::Sdk { .. } | WslcError::Host(_) => FailurePhase::LaunchFailed,
// The container was up but the run broke.
WslcError::Runtime(_) => FailurePhase::PostLaunchFailed,

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment threadsrc/backends/wslc/common/src/wsl_container_runner.rs Outdated
Comment threadsrc/core/mxc_engine/src/dispatch.rs
Comment on lines +6 to +12
//! WSLc previously built every failure as a free-text [`ScriptResponse`], which
//! left `failure_phase` at its `None` default. Because
//! `mxc_engine::dispatch::map_spawn_error` discriminates on exactly that field,
//! every WSLc failure reached the Rust SDK as an opaque `backend_error` — so a
//! caller could only tell a missing-WSL host from a rejected policy by parsing
//! the message. Each variant here attributes the failure to a lifecycle phase
//! instead.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Folded into #843

CopilotAI review requested due to automatic review settings August 25, 2026 22:13

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 5 out of 5 changed files in this pull request and generated no new comments.

Suppressed comments (2)

Previously missed (2) — in code that hasn't changed since the last review.

src/backends/wslc/common/src/container_steps.rs:676

  • This phase is dropped on the state-aware path. load_sdk_checked is called by wslc/daemon/src/session_manager.rs:357, but that file's sr_err converts the response to anyhow!(resp.error_message) and every such failure becomes WorkerError::Backend/ErrKind::Backend. Consequently a missing SDK or prerequisite still reaches run_state_aware_json as backend_error, so the advertised typed distinction only works for one-shot/streaming. Preserve this classification through WorkerError, ErrKind, and map_daemon_error rather than erasing it at the daemon boundary.
 let sdk = WslcSdk::load().map_err(|e| WslcError::Unavailable(e).into_response())?;
let mut missing = WslcComponentFlags::WSLC_COMPONENT_FLAG_NONE;
let hr = sdk.WslcGetMissingComponents(&mut missing);
if hr != S_OK {
return Err(sdk_error("WslcGetMissingComponents failed", hr, ""));
}
if missing.any_missing() {
return Err(WslcError::Unavailable(wslc_prerequisite_error(missing)).into_response());

src/backends/wslc/common/src/error.rs:67

  • Classifying by error source rather than lifecycle point makes several new phases incorrect. For example, wait_for_process calls sdk_error for WslcGetProcessExitEvent and WslcGetProcessExitCode after the process exists (wsl_container_runner.rs:1106,1198), while adjacent wait failures correctly use Runtime; state-aware exec has the same pattern in container_steps.rs:945,1035. Those SDK failures are now reported as LaunchFailed instead of PostLaunchFailed. Split SDK errors into launch/runtime variants (or pass the phase at construction) and classify each call site according to whether process creation already succeeded.
 // The SDK call or host bring-up failed; generally worth retrying.
WslcError::Sdk { .. } | WslcError::Host(_) => FailurePhase::LaunchFailed,
// The container was up but the run broke.
WslcError::Runtime(_) => FailurePhase::PostLaunchFailed,

@SohamDas2021
Soham Das (SohamDas2021) merged commit e78720c into mainAug 26, 2026
27 of 28 checks passed
@SohamDas2021
Soham Das (SohamDas2021) deleted the user/sodas/794-wslc-typed-errors branch August 26, 2026 05:32
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[WSLC] Encapsulate ScriptResponse errors in a typed enum for the Rust SDK

4 participants

@SohamDas2021@dhoehna@bbonaby