Skip to content

Recover extensions after WebAssembly traps - #59

Merged
kjanat merged 1 commit into
masterfrom
fix/extension-trap-recovery
Sep 5, 2026
Merged

Recover extensions after WebAssembly traps#59
kjanat merged 1 commit into
masterfrom
fix/extension-trap-recovery

Conversation

@kjanat

@kjanat kjanat commented Sep 5, 2026

Copy link
Copy Markdown
Owner

Objective

Fixes #58.

A WebAssembly trap currently leaves an extension unusable until reload or editor restart. Subsequent calls keep entering the poisoned store and fail with cannot enter component instance, as reported with the Ruby extension zed-extensions/ruby#323.

Solution

Discard both the store and component instance after a Wasmtime trap. Initialize a fresh pair before serving the next queued call, retaining the compiled component and existing call queue. Return the original trap to the failed caller without replaying an operation that may already have performed side effects.

Ordinary extension errors retain the instance. Recovery failures reach the caller and leave the worker available to attempt initialization on the next request. Caller cancellation does not interrupt an in-flight invocation.

Testing

  • cargo test -p extension_host wasm_host::tests:: -- --nocapture: all 5 tests passed on Linux, including 4 new regressions using a real WebAssembly component.
  • Coverage includes queued-call recovery, the original trap's type, fresh initialization, ordinary errors, initialization failure, and caller cancellation.
  • dprint check crates/extension_host/src/wasm_host.rs crates/extension_host/Cargo.toml and git diff --check passed.
  • A rebuilt editor with the Ruby extension was not exercised.

Self-Review Checklist

  • Reviewed the diff for correctness and reliability.
  • Tests cover the changed behavior.
  • Recovery reuses the compiled component; normal calls do not reinitialize it.
  • No new unsafe blocks or UI changes.

Suggested .rules additions

Keep #[gpui::test] functions returning (). The macro discards a returned Result, so explicitly assert or unwrap an async helper's result to ensure setup and execution errors fail the test.

Release Notes:

  • Fixed extensions remaining unusable after a WebAssembly trap by recreating their runtime before the next call.

Discard poisoned stores after traps and initialize a fresh component
for the next queued call. Preserve the original error without replaying
operations that may already have performed side effects.

Cover recovery, ordinary errors, initialization failures, and caller
cancellation with real WebAssembly regression tests.

Fixes #58.
@kjanat kjanat added bug Something isn't working extension-host WebAssembly extension execution and lifecycle labels Sep 5, 2026
@kjanat kjanat self-assigned this Sep 5, 2026
@kjanat kjanat added the cr:review Allow CodeRabbit review label Sep 5, 2026
@coderabbitai

coderabbitai Bot commented Sep 5, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Essentials

Run ID: 85d1db1f-23f1-41e3-a245-70a2f1dcf8be

📥 Commits

Reviewing files that changed from the base of the PR and between 413c868 and d203c69.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (2)
  • crates/extension_host/Cargo.toml
  • crates/extension_host/src/wasm_host.rs

Included review availability: 1 review is currently available. Your included PR review attempts over the past 7 days set your current allowance at 3 reviews per hour.


📝 Summary

Recover Wasm extensions after runtime traps

Discard the poisoned store and component instance after a trap, then initialise fresh instances before processing queued calls. Preserve the original trap, avoid replaying failed operations, and surface recovery failures.

Keep ordinary extension errors separate from runtime traps. Ensure queued calls complete when callers cancel their requests.

Add regression tests for recovery, fresh initialisation, ordinary errors, recovery failures, and cancellation.

Walkthrough

The extension host now propagates Result<T> values through queued Wasm calls. Traps invalidate the current instance and trigger re-instantiation. Ordinary errors preserve the instance. Tests cover recovery, recovery failure, state preservation, and caller cancellation.

Changes

Wasm extension recovery

Layer / File(s) Summary
Result-bearing extension calls
crates/extension_host/src/wasm_host.rs
Wrappers and WasmExtension::call now propagate callback results directly and add extension context to errors.
Instance recovery queue
crates/extension_host/src/wasm_host.rs, crates/extension_host/Cargo.toml
The worker reports call results, recreates the Wasm instance after traps, and reuses initialisation for replacements.
Recovery regression coverage
crates/extension_host/src/wasm_host.rs
Component tests cover ordinary errors, trap recovery, recovery-initialisation failures, and completion after caller cancellation.

Estimated code review effort: 4 (Complex) | ~45 minutes

Merge Risk: ⚪ Minimal · up to d203c

The change restores extension operation after WebAssembly traps while preserving ordinary error behavior, with no substantiated merge-blocking issue remaining.

Sequence Diagram(s)

sequenceDiagram
  participant Caller
  participant WasmExtension
  participant ExtensionWorker
  participant WasmInstance
  Caller->>WasmExtension: Queue Result-returning callback
  WasmExtension->>ExtensionWorker: Send callback
  ExtensionWorker->>WasmInstance: Execute callback
  WasmInstance-->>ExtensionWorker: Return result or trap
  ExtensionWorker->>WasmInstance: Instantiate replacement after trap
  ExtensionWorker-->>Caller: Return execution or recovery result
Loading

Poem

The trapped Wasm store now sails anew.
Queued calls return their results true.
Ordinary errors leave state in place.
Recovery failures show their face.
Cancelled callers still get work done.
The extension host has its second sun.

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise and directly describes recovery after WebAssembly traps. It is not a full sentence.
Description check ✅ Passed The description includes the objective, solution, testing details, self-review checklist, and release notes. It also documents the untested Ruby extension scenario.
Linked Issues check ✅ Passed The changes address issue #58. They preserve the original trap, discard the poisoned runtime, initialise a fresh store and component before later calls, distinguish ordinary errors, surface recovery f…
Out of Scope Changes check ✅ Passed The reviewed changes are within scope. The development dependency supports the added WebAssembly regression tests, and the runtime changes implement extension recovery after traps.
✨ Finishing Touches
✨ Simplify code
  • Create PR with simplified code
  • Commit simplified code in branch fix/extension-trap-recovery

Comment @coderabbitai help to get the list of available commands.

@kjanat
kjanat merged commit 04d5703 into master Sep 5, 2026
14 checks passed
@kjanat
kjanat deleted the fix/extension-trap-recovery branch September 5, 2026 21:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working cr:review Allow CodeRabbit review extension-host WebAssembly extension execution and lifecycle

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Recover extensions after WebAssembly traps

1 participant