Skip to content

Python: Fix hyperlight WasmSandbox cross-thread Drop and harden hosted-agent sample - #5603

Merged
Eduard van Valkenburg (eavanvalkenburg) merged 8 commits into
microsoft:mainfrom
eavanvalkenburg:update_hyperlight
May 5, 2026
Merged

Python: Fix hyperlight WasmSandbox cross-thread Drop and harden hosted-agent sample#5603
Eduard van Valkenburg (eavanvalkenburg) merged 8 commits into
microsoft:mainfrom
eavanvalkenburg:update_hyperlight

Conversation

@eavanvalkenburg

@eavanvalkenburg Eduard van Valkenburg (eavanvalkenburg) commented May 1, 2026

Copy link
Copy Markdown
Member

Motivation and Context

The hyperlight package intermittently raised
RuntimeError: _native_wasm::WasmSandbox is unsendable, but is being dropped on another thread
in deployed environments, even after an actor-model refactor that pinned the
unsendable PyO3 WasmSandbox and PySnapshot to a single worker thread.

Root cause: when a worker-side closure raised, the exception's __traceback__
retained frame locals that included the partially constructed PyO3 sandbox.
Future.result() re-raised that exception on the caller thread, and when the
caller's exception was eventually GC'd the frame locals were released
off-thread, dec_ref'ing the unsendable sandbox from the wrong thread and
tripping the PyO3 panic.

Description

Hyperlight package

  • _SandboxWorker._run_on_worker now wraps every worker-thread submission:
    it catches every exception on the worker, drops __traceback__ there,
    deletes the original exception, and re-raises a fresh instance on the
    caller thread. initialize and execute route through it; dispose
    keeps its bare-submit semantics.
  • New tests cover cross-thread invocation, exception/traceback isolation,
    the architectural attribute-shape of _SandboxEntry, and a stale-reference
    stress test driven through asyncio.to_thread.

Sample (samples/04-hosting/container/hyperlight_codeact)

  • Dockerfile now installs agent-framework-* from in-tree source with
    python/ as the build context, so unreleased fixes can be validated
    end-to-end.
  • call_server.py pins the Responses API version.
  • main.py enables include_detailed_errors=True so future tool failures
    surface the actual exception text instead of a bare Error: Function failed. string.
  • README.md documents the in-tree-package build flow and the Hyperlight
    hypervisor requirement (/dev/kvm on Linux, MSHV on Windows). Hosted
    environments without hypervisor passthrough surface
    No Hypervisor was found for Sandbox — that is a hosting constraint, not
    a hyperlight bug.

Contribution Checklist

  • The code builds clean without any errors or warnings
  • The PR follows the Contribution Guidelines
  • All unit tests pass, and I have added new tests where possible
  • Is this a breaking change? No.

Copilot AI review requested due to automatic review settings May 1, 2026 15:03
@moonbox3 Evan Mattson (moonbox3) added documentation Usage: [Issues, PRs], Target: documentation in the code base and learn docs python Usage: [Issues, PRs], Target: Python labels May 1, 2026
@moonbox3

Evan Mattson (moonbox3) commented May 1, 2026

Copy link
Copy Markdown
Contributor

Python Test Coverage

Python Test Coverage Report •
FileStmtsMissCoverMissing
packages/hyperlight/agent_framework_hyperlight
   _execute_code_tool.py5498185%67, 169, 232, 264, 267, 302–303, 318, 320, 333, 351, 361, 386, 391, 398, 404, 412, 420–422, 424–429, 469, 474, 476, 478, 495–496, 505–508, 535–538, 544, 546, 556–557, 589–590, 593–594, 601, 647, 703–709, 778, 805, 875, 911, 917–919, 948–952, 956–957, 962, 979–983, 987–988, 1045–1046
TOTAL31894369688% 

Python Unit Test Overview

Tests Skipped Failures Errors Time
6272 30 💤 0 ❌ 0 🔥 1m 41s ⏱️

@github-actions github-actions Bot 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.

Automated Code Review

Reviewers: 4 | Confidence: 90%

✓ Correctness

The core refactoring of _SandboxWorker into an actor model is well-designed and correct. Unsendable PyO3 objects are properly confined to the worker thread via closures, exception tracebacks are sanitized to prevent wrong-thread drops, and _result_snapshot ensures Content.raw_representation carries only sendable plain dicts. The lazy-loading agent_framework.hyperlight namespace and new regression tests are sound. One sample file (call_server.py) hardcodes a developer-specific Azure endpoint URL that should be parameterized.

✓ Security Reliability

The core refactoring of _SandboxWorker to an actor model is well-designed and correctly addresses the PyO3 unsendable Drop-on-wrong-thread panic. Exception traceback sanitization, _result_snapshot for raw_representation, and worker-confined dispose are all sound. The one actionable issue is a hardcoded developer-specific Azure endpoint in the new call_server.py sample, which every other sample in the repo avoids by using placeholders or environment variables.

✓ Test Coverage

The PR adds a significant actor-model refactor to _SandboxWorker with five well-crafted regression tests covering the core threading/Drop safety invariants. However, the new agent_framework.hyperlight lazy-loading namespace module (python/packages/core/agent_framework/hyperlight/__init__.py) has zero test coverage. The repo has an established pattern for testing these namespace modules (see test_foundry_namespace.py, test_azure_namespace.py), and the new module has three distinct code paths (successful lazy import, ModuleNotFoundError for missing packages, AttributeError for unknown names) — none of which are tested. Additionally, _SandboxWorker.dispose() documents 'safe to call multiple times' but has no test exercising that idempotency guarantee.

✗ Design Approach

The sandbox actor refactor itself looks like it is addressing the right root cause, but the new hosted-sample surface has two design problems: it advertises Hyperlight as a normal Foundry hosted-agent sample even though the sample README says the default hosted-agent runtime cannot provide the required hypervisor/device passthrough, and the companion client script is hard-wired to one specific deployed agent URL instead of demonstrating the reusable invocation pattern used elsewhere in this repo.

Flagged Issues

  • python/samples/04-hosting/foundry-hosted-agents/README.md:16 adds 06_hyperlight_codeact as a standard Foundry hosted-agent sample, but the sample's own README (responses/06_hyperlight_codeact/README.md:27-49) says Hyperlight needs /dev/kvm/nested virtualization and that the default Foundry hosted-agent runtime will generally not work. That is the wrong abstraction for this sample; it should live in a self-managed/nested-virt-specific area or be explicitly separated from the normal hosted-agent path.

Automated review by eavanvalkenburg's agents

Copilot AI 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

This PR addresses a Python Hyperlight sandbox lifetime/thread-affinity bug that could surface as cross-thread PyO3 unsendable drops, and adds a Foundry-hosted agent sample for end-to-end validation of Hyperlight CodeAct.

Changes:

  • Reworked Hyperlight sandbox execution to keep sandbox/snapshot/result objects thread-confined and sanitize worker-thread exceptions (dropping tracebacks on the worker).
  • Added/expanded regression tests for cross-thread drop scenarios and exception traceback isolation.
  • Added a new Foundry hosted-agent sample (Responses protocol) and introduced a lazy-loading agent_framework.hyperlight namespace for optional Hyperlight integrations.

Reviewed changes

Copilot reviewed 24 out of 25 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
python/uv.lock Adds Hyperlight package to extras and updates markers/locked versions for related deps.
python/samples/04-hosting/foundry-hosted-agents/responses/06_hyperlight_codeact/requirements.txt Adds sample-only requirements placeholder (local installs handled by Dockerfile).
python/samples/04-hosting/foundry-hosted-agents/responses/06_hyperlight_codeact/main.py Adds a hosted Responses server sample using Hyperlight CodeAct.
python/samples/04-hosting/foundry-hosted-agents/responses/06_hyperlight_codeact/call_server.py Adds a script to call the deployed hosted agent via OpenAI client.
python/samples/04-hosting/foundry-hosted-agents/responses/06_hyperlight_codeact/agent.yaml Adds hosted-agent deployment descriptor for the sample.
python/samples/04-hosting/foundry-hosted-agents/responses/06_hyperlight_codeact/agent.manifest.yaml Adds manifest template variant for deployment.
python/samples/04-hosting/foundry-hosted-agents/responses/06_hyperlight_codeact/README.md Documents the sample, CodeAct flow, and Hyperlight hypervisor requirements.
python/samples/04-hosting/foundry-hosted-agents/responses/06_hyperlight_codeact/Dockerfile Builds a container that installs in-tree Agent Framework + Hyperlight packages.
python/samples/04-hosting/foundry-hosted-agents/responses/06_hyperlight_codeact/.env.example Adds env var template for Foundry endpoint/model deployment name.
python/samples/04-hosting/foundry-hosted-agents/responses/06_hyperlight_codeact/.dockerignore Adds a sample-local dockerignore.
python/samples/04-hosting/foundry-hosted-agents/README.md Updates the sample index to include the new Hyperlight CodeAct sample.
python/samples/02-agents/tools/local_code_interpreter/local_code_interpreter_manual_wiring.py Switches sample import to agent_framework.hyperlight lazy namespace.
python/samples/02-agents/tools/local_code_interpreter/local_code_interpreter.py Switches sample import to agent_framework.hyperlight lazy namespace.
python/samples/02-agents/tools/local_code_interpreter/README.md Adds documentation for the local Hyperlight code interpreter samples.
python/samples/02-agents/context_providers/code_act/code_act.py Switches sample import to agent_framework.hyperlight lazy namespace.
python/samples/02-agents/context_providers/code_act/README.md Adds documentation for the Hyperlight CodeAct context provider sample.
python/packages/hyperlight/tests/hyperlight/test_hyperlight_codeact.py Adds regression tests for thread-confined drops and exception traceback handling.
python/packages/hyperlight/samples/codeact_benchmark.py Removes package-local benchmark sample.
python/packages/hyperlight/samples/README.md Removes package-local samples README.
python/packages/hyperlight/pyproject.toml Bumps Hyperlight package version/status and adjusts tooling config.
python/packages/hyperlight/agent_framework_hyperlight/_execute_code_tool.py Implements actor-style sandbox worker, result snapshotting, and traceback sanitization.
python/packages/hyperlight/README.md Updates package README notes to reflect lazy-loading namespace availability.
python/packages/core/pyproject.toml Adds Hyperlight to the core all extra behind platform/Python version markers.
python/packages/core/agent_framework/hyperlight/init.py Adds lazy re-export namespace for agent-framework-hyperlight.
python/PACKAGE_STATUS.md Updates Hyperlight package status from alpha to beta.

Comment thread python/packages/hyperlight/tests/hyperlight/test_hyperlight_codeact.py Outdated
Comment thread python/packages/hyperlight/agent_framework_hyperlight/_execute_code_tool.py Outdated
Root cause: when a worker-side closure raised, the exception's __traceback__
retained frame locals that included the partially constructed PyO3 sandbox.
Future.result() re-raised that exception on the caller thread, and when the
caller's exception was eventually GC'd the frame locals were released
off-thread, dec_ref'ing the unsendable sandbox from the wrong thread and
tripping the PyO3 panic
'_native_wasm::WasmSandbox is unsendable, but is being dropped on another thread'.

Fix:
* Add _SandboxWorker._run_on_worker which catches every exception on the
  worker, drops __traceback__ there, deletes the original exception, and
  re-raises a fresh instance on the caller thread. initialize and execute
  route through it; dispose keeps its bare-submit semantics.
* Add an opt-in diagnostic module _drop_diagnostic (no-op unless
  HYPERLIGHT_TRACE_DROPS=1) that installs a sys.unraisablehook and dumps
  owner-thread + per-thread stacks on any future cross-thread unsendable
  Drop. Useful for triaging similar PyO3 regressions.
* Tests: cross-thread invocation, traceback-leak isolation, _SandboxEntry
  attribute-shape check, and a stale-reference stress test driven through
  asyncio.to_thread.

Sample (samples/04-hosting/foundry-hosted-agents/responses/06_hyperlight_codeact):
* Dockerfile installs agent-framework-* from in-tree source with python/ as
  build context so unreleased fixes can be validated end-to-end.
* call_server.py pins the Responses API version.
* main.py enables include_detailed_errors=True so future tool failures
  surface the actual exception text instead of a bare 'Error: Function
  failed.' string.
* README.md documents the in-tree-package build and the Hyperlight
  hypervisor requirement (/dev/kvm on Linux, MSHV on Windows). Hosted
  environments without hypervisor passthrough surface 'No Hypervisor was
  found for Sandbox'; this is a hosting constraint, not a hyperlight bug.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The diagnostic module was useful while bisecting the cross-thread Drop bug,
but it is no longer needed now that _SandboxWorker._run_on_worker prevents
the panic at the source.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Use lazy agent_framework.hyperlight import in sample main.py.
- Env-driven endpoint (FOUNDRY_AGENT_ENDPOINT) in call_server.py; remove personal URLs.
- Align agent.yaml model deployment with manifest (gpt-4.1-mini).
- Tighten Dockerfile requirements guard; drop dangling deploy.ps1 reference.
- Preserve exception args when sanitizing tracebacks in _run_on_worker.
- Add public _SandboxWorker.is_alive(); update test to avoid private attr.
- Add namespace coverage tests for agent_framework.hyperlight lazy loader.
- Add prominent note: Foundry hosted-agent runtime does not yet support
  Hyperlight (no hypervisor exposed); container works locally with /dev/kvm.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Stringify exc.args on the worker thread before propagating, so any
PyO3 unsendable object captured in args (e.g. via a caller-supplied
callback or underlying SDK) cannot be Dropped on the calling thread.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Comment thread python/samples/04-hosting/container/hyperlight_codeact/README.md
Merged via the queue into microsoft:main with commit 57c901a May 5, 2026
34 of 36 checks passed
@eavanvalkenburg
Eduard van Valkenburg (eavanvalkenburg) deleted the update_hyperlight branch June 30, 2026 09:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Usage: [Issues, PRs], Target: documentation in the code base and learn docs python Usage: [Issues, PRs], Target: Python

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants