Skip to content

fix: host_bindgen! returns Result instead of panicking on guest errors#1317

Open
jsturtevant wants to merge 1 commit into
mainfrom
rally/1316-host-bindgen-generated-code-panics-on-guest-error
Open

fix: host_bindgen! returns Result instead of panicking on guest errors#1317
jsturtevant wants to merge 1 commit into
mainfrom
rally/1316-host-bindgen-generated-code-panics-on-guest-error

Conversation

@jsturtevant

@jsturtevant jsturtevant commented Mar 17, 2026

Copy link
Copy Markdown
Contributor

fixes: #1316

host_bindgen! export wrappers no longer panic when Callable::call() returns Err (guest abort/trap/timeout). Host callers now receive the HyperlightError.

Breaking change

Host-side guest export traits are now fallible and use a *Exports suffix. Host functions imported by the guest keep the original WIT signatures.

Example WIT:

world test {
  import roundtrip;
  export roundtrip;
}

interface roundtrip {
  echo: func(x: string) -> string;
}

Generated/used Rust shape:

// Guest -> host import implementation: unchanged.
impl test::wit::Roundtrip for Host {
    fn echo(&mut self, x: String) -> String { x }
}

// Host -> guest export call: renamed trait and fallible result.
use test::wit::{RoundtripExports, TestExports};

let value: Result<String, HyperlightError> = sandbox.roundtrip().echo("hi".into());

Signed-off-by: James Sturtevant jsturtevant@gmail.com

@jsturtevant jsturtevant force-pushed the rally/1316-host-bindgen-generated-code-panics-on-guest-error branch from 21ca91e to eadb9f1 Compare March 17, 2026 21:19
@jsturtevant jsturtevant added the kind/bugfix For PRs that fix bugs label Mar 17, 2026

@ludfjig ludfjig 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.

I think I am fine with this, but I suspect this is intentional. I think we generally want to avoid Result types as much as possible where it makes sense, but since this matches the current guest calling convention maybe let's defer that discussion to later

@jsturtevant

Copy link
Copy Markdown
Contributor Author

I think I am fine with this, but I suspect this is intentional. I think we generally want to avoid Result types as much as possible where it makes sense, but since this matches the current guest calling convention maybe let's defer that discussion to later

Open to other ways of addressing this I guess but a panic in the guest shouldn't panic this host. It seemed practical to match existing behavoir

@syntactically

Copy link
Copy Markdown
Member

I agree with @ludfjig that, generally speaking, the semantic gap of lifting everything from the interface into Option isn't particularly attractive. However, I do see the need to deal with errors that fundamentally can happen during execution on the host side.

Is there a reason that this is also applied to the host functions imported by the guest? I think that generally speaking it is the ability to export a function implementing an interface, but actually implement a different interface (i.e. one with more optionality) that is the most semantically damaging---so the implicit Results are particularly unattractive on guest->host function calls (and to some extent in the implementation of guest functions, although the ability to trap/panic/etc is perhaps equivalent). It seems like splitting this (so that host->guest function calls can return errors, but host functions can't be implemented by returning errors) is what is common in the prior art e.g. the wasmtime host bindgen. Is it just about continuing to be able to use the same trait for the import and export versions of an interface? Maybe a type parameter to track whether this is an imported or exported usage would make sense?

Let me know what you think about the usability of that. As I think about this more, from the wasm perspective especially, I do think that having the Results on host function calls is slightly unattractive, but it's not quite as bad as I would have guessed, since from wasm's perspective, a (wasm) import that's called always does in fact have the option of causing the entire world to be torn town via purposefully causing a trap, and this is perhaps in some ways similar behaviour.

@jsturtevant

Copy link
Copy Markdown
Contributor Author

Is there a reason that this is also applied to the host functions imported by the guest?

This was to match the current non-wit implementation. We had improved the situation with errors in #868.

I think that generally speaking it is the ability to export a function implementing an interface, but actually implement a different interface (i.e. one with more optionality) that is the most semantically damaging---so the implicit Results are particularly unattractive on guest->host function calls (and to some extent in the implementation of guest functions, although the ability to trap/panic/etc is perhaps equivalent). It seems like splitting this (so that host->guest function calls can return errors, but host functions can't be implemented by returning errors) is what is common in the prior art e.g. the wasmtime host bindgen.

Agree with the general sentiment that's its not ideal. I am not so sure we don't want host function to return errors, it seems useful to me to be able to say something went wrong across the boundry. Be interesting in what others think

@jsturtevant

Copy link
Copy Markdown
Contributor Author

Agree with the general sentiment that's its not ideal. I am not so sure we don't want host function to return errors, it seems useful to me to be able to say something went wrong across the boundry. Be interesting in what others think

actually, was thinking on this some more and WIT does have the ability capture "result" types so we probalby don't need this on the guest side (I don't see the equivalent ::std::result::Result::Ok(#ret) = #ret else { panic!("bad return from guest {:?}", #ret) }; on the Guest->Host calls. Maybe there is another way to handle that too... I am open to ideas just really think we shouldn't panic if guest traps.

@syntactically

Copy link
Copy Markdown
Member

just really think we shouldn't panic if guest traps

I'm with you on that one :)

This was to match the current non-wit implementation. We had improved the situation with errors in #868.

I am not so sure we don't want host function to return errors, it seems useful to me to be able to say something went wrong across the boundry.

I'm still not totally convinced on this, although I can see the arguments. Can you elaborate a little more on the use cases you see? If we did allow this, I think it's important that it not be semantically visible in the guest, which shouldn't know whether its imported component is implemented across a hyperlight-host boundary or by any other component, so we would need to propagate the error return up, and I would think that it would probably make sense to poison the sandbox (probably by panicking the guest?). If there's not a super strong use case, having the optionality just on the host->guest calls and not the hostcall returns seems like it is a starting point that gets rid of the main issue and is minimally invasive?

actually, was thinking on this some more and WIT does have the ability capture "result" types so we probalby don't need this on the guest side (I don't see the equivalent ::std::result::Result::Ok(#ret) = #ret else { panic!("bad return from guest {:?}", #ret) }; on the Guest->Host calls. Maybe there is another way to handle that too...

There is an unwrap on the host call result here.

Precisely the thing that I want to be careful about here is conflating WIT-level semantically-there-in-the-API optionality with this unconditionally-added-everywhere Hyperlight-level optionality. As I said in the paragraph above, even if we did add this for host function return types, I would not want to see it meaningfully exposed to the guest code.

@jsturtevant

Copy link
Copy Markdown
Contributor Author

I am not so sure we don't want host function to return errors, it seems useful to me to be able to say something went wrong across the boundry.

I'm still not totally convinced on this, although I can see the arguments. Can you elaborate a little more on the use cases you see? If we did allow this, I think it's important that it not be semantically visible in the guest, which shouldn't know whether its imported component is implemented across a hyperlight-host boundary or by any other component, so we would need to propagate the error return up, and I would think that it would probably make sense to poison the sandbox (probably by panicking the guest?). If there's not a super strong use case, having the optionality just on the host->guest calls and not the hostcall returns seems like it is a starting point that gets rid of the main issue and is minimally invasive?

Yes I am in agree that we shouldn't do this now. I'm going to revisit this and see if I can come up with something different for consideration.

@ludfjig

ludfjig commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

@jsturtevant should we close this one?

@jsturtevant

Copy link
Copy Markdown
Contributor Author

We need a version of this, let me see if I can fix it up this week

Copilot AI review requested due to automatic review settings June 19, 2026 23:01
@jsturtevant jsturtevant force-pushed the rally/1316-host-bindgen-generated-code-panics-on-guest-error branch from eadb9f1 to 498499a Compare June 19, 2026 23:01

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 updates the host_bindgen!-generated host-side export wrappers so that guest call failures (e.g., abort/trap/timeout) are surfaced to callers as Result<_, HyperlightError> instead of crashing the host via panic!. This aligns exported guest calls with other Hyperlight call paths that already return errors.

Changes:

  • Change host export wrapper generation to propagate Callable::call() failures via ? and return Ok(...) on success.
  • Update generated host-side export trait method signatures to return Result<T, HyperlightError> (while keeping constructors unaffected).
  • Update tests to unwrap successful guest calls and add a synthetic error-propagation test; also add some codegen deduplication state to avoid duplicate emitted types.

Reviewed changes

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

Show a summary per file
File Description
src/hyperlight_host/tests/wit_test.rs Updates callers for new Result-returning export methods and adds a test ensuring errors are returned instead of panicking.
src/hyperlight_component_util/src/rtypes.rs Changes generated host-side export trait method return types to Result<_, HyperlightError> and adjusts export-instance trait naming.
src/hyperlight_component_util/src/resource.rs Makes generated resource-table new() constructor pub(crate) (needed by new/updated usage patterns).
src/hyperlight_component_util/src/host.rs Removes panic! on guest call errors in generated wrappers; returns Result and propagates failures with ?.
src/hyperlight_component_util/src/emit.rs Adds module-level tracking (emitted_type_names) to prevent duplicate type emissions during codegen.

Comment thread src/hyperlight_component_util/src/rtypes.rs
Comment thread src/hyperlight_host/tests/wit_test.rs
Make generated host-side WIT export calls return HyperlightError instead of panicking when guest execution fails, while keeping guest-imported host function traits non-fallible. Add a real WIT guest panic export to cover the host error path.

Signed-off-by: James Sturtevant <jsturtevant@gmail.com>
@jsturtevant jsturtevant force-pushed the rally/1316-host-bindgen-generated-code-panics-on-guest-error branch from cefb1ac to cae31c2 Compare June 19, 2026 23:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

kind/bugfix For PRs that fix bugs

Projects

None yet

Development

Successfully merging this pull request may close these issues.

host_bindgen! generated code panics on guest errors instead of returning Result

4 participants