Skip to content

C++ bindgen: function with two own<resource> args returns an error handle #1684

Open

Description

@xiaozzzZZzzz240

Summary: C++ provider component lifts a non-empty string return value as an empty string.

A wit-bindgen-cpp bug where a C++ provider component returns an empty string in place of the actual string value (e.g. "temres26"), observed consistently across all consumer languages.

Environment

component version
OS macOS 15.7 (Darwin 24.6.0), x86_64
jco 1.16.1 (source checkout, tag jco-v1.16.1)
Node.js v22.22.2
wac wac-cli 0.8.1
wasi-sdk 27.0 (wasm32-wasip2-clang/clang++)

Steps to reproduce

The full chain is: compile the guest sources into a provider component and a consumer component, compose them with wac, then run the composed component. Prebuilt copies of every artifact are included (components/provider.wasm, components/consumer.wasm, composed.wasm), so you can also skip straight to step 5.

Github repo:https://github.com/xiaozzzZZzzz240/G051-Cplusplus-string-return-from-a-resource-method-comes-back-empty

  1. Clone this repository:

    git clone <THIS_REPO_URL>
    cd <THIS_REPO_DIR>
  2. Compile the provider component from the C++ sources in src/provider-Cpp/ (toolchain versions in the table above; the WIT package is in wit/):

    wasm32-wasip2-clang++ -std=c++23 -fno-exceptions -o components/provider.wasm -mexec-model=reactor src/provider-Cpp/world69-uvfx7r7j_generated.cpp src/provider-Cpp/world69_uvfx7r7j.cpp src/provider-Cpp/world69_uvfx7r7j_component_type.o
  3. Compile the consumer component from the C sources in src/consumer-C/:

    wasm32-wasip2-clang -o components/consumer.wasm -mexec-model=reactor src/consumer-C/world69b-myhp0jr6_generated.c src/consumer-C/world69b_myhp0jr6.c src/consumer-C/world69b_myhp0jr6_component_type.o
  4. Compose the two components into one runnable component with wac 0.8.1:

    wac plug components/consumer.wasm --plug components/provider.wasm -o composed.wasm
  5. Run the composed component with jco:

    npm install -g @bytecodealliance/jco@1.16.1
    jco run composed.wasm

Expected result

The composed component should print exactly the following, then exit 0:

"resinst0"
"resinst0"
"foo"
true
true
"temres132"
resource("temres26")
([resource("temres28")], "foo")
true
resource("temres160")
resource("temres161")
true
"temres26"
"temres32"
7
"p"

Actual result

Under jco the same composed.wasm exits with rc=0. stdout:

"resinst0"
"resinst0"
"foo"
true
true
"temres132"
resource("temres26")
([resource("temres28")], "foo")
true
resource("temres160")
resource("temres161")
true
""
"temres32"
7
"p"

stderr:

(empty)

wasmtime on the exact same file: rc=1

"resinst0"
"resinst0"
"foo"
true
true
"temres132"
resource("temres26")
([resource("temres28")], "foo")
true
resource("temres160")
resource("temres161")
true

--- stderr ---
Error: failed to run main module `/Users/myuser/WASM/myProject/Wasm_component_testing/mycode/execute/reportrepo/G051-wit-bindgen-cpp-returns-empty-string-instead-of-original-value/composed.wasm`

Caused by:
    0: failed to invoke `run` function
    1: error while executing at wasm backtrace:
           0:    0x5fe - <unknown>!<wasm function 17>
           1:  0xa18f9 - wit-component:shim!indirect-mydefined:package3hl16smu1p/itf-n3bcdaqpyw-f-ywm7quh
           2:  0x64eca - consumer.wasm!mydefined_package3hl16smu1p_itf_n3bcdaqpyw_f_ywm7quh
           3:  0x642eb - consumer.wasm!exports_wasi_cli_run_run
           4:  0x64fd1 - consumer.wasm!__wasm_export_exports_wasi_cli_run_run
       note: using the `WASMTIME_BACKTRACE_DETAILS=1` environment variable may show more debugging information
    2: unknown handle index 521124

Activity

  1. tschneidereit commented on Aug 17, 2026

    @tschneidereit
    Member

    @xiaozzzZZzzz240 once more, please ensure that your reports adhere to our requirements. I assume the G051- prefix in the title here is something an LLM created as part of a campaign, and not something that's relevant to us. And the title as a whole is not very human-readable.

    Additionally, and in this case more importantly, the reproducer in the repo you linked to is very far from reduced. Looking at just this directory, there are many thousands of lines of generated code and a .o binary file.

    Based on the description this seems like it should be a bug that can be reduced to a single component with far fewer lines of code. But if that's wrong and it really needs interactions between two components, then it should still be reduced to what's truly required.

    As-is, a maintainer would have to spend a truly significant amount of time to investigate this and similar reports, which we're unlikely to have the time for.

  2. changed the title [-]G051-Cplusplus-string-return-from-a-resource-method-comes-back-empty[/-] [+]C++ bindgen: function with two `own<resource>` args returns an error handle[/+] on Aug 18, 2026
  3. xiaozzzZZzzz240 commented on Aug 18, 2026

    @xiaozzzZZzzz240
    Author

    Thanks for your feedback. Sorry for the misunderstanding.
    The G051- prefix was added by myself to help me locate bugs, it has been dropped and the issue retitled.
    And I tried my best to minimize the test case. You can only focus on the following minimized bug report. And I did try to reduce the test case which contains provider and consumer to a single component, but the bug only occurs when a resource handle crosses the component boundary and later re-enters as an own parameter, thus intra-component calls never go through the canonical ABI. Thus, I have to remain the provider consumer structure to reproduce the bug.

    Minimized Bug Report

    Summary

    The exported function f: func(a0: own, a1: own) -> own returns std::move(a0), on the provider side, but the caller gets a garbage returned handle.
    The bug is in the generated C++ glue code. Each resource object stores its handle index at construction time. The glue never updates this index afterwards. The unused a1 gets dropped at the end of f. The glue frees a1's old index. But that index now belongs to a0. So dropping a1 destroys a0 by mistake. f then returns -1. wasmtime rejects this handle and traps with unknown handle index 4294967295. jco does not check the handle. A later get-name call reads freed memory and prints "". And if f takes only one parameter (f: func(a0: own) -> own, same provider body), both runtimes work fine.

    Environment

    component version
    OS macOS 15.7 (Darwin 24.6.0), x86_64
    wit-bindgen wit-bindgen-cli 0.51.0 (cpp and c backends)
    wasi-sdk 27.0 (wasm32-wasip2-clang/clang++)
    wac wac-cli 0.8.1
    wasmtime 41.0.0
    jco 1.16.1 (source checkout, tag jco-v1.16.1)
    Node.js v22.22.2

    Steps to reproduce

    Github repo:https://github.com/xiaozzzZZzzz240/wit-bindgen-1684-repo

    1. Generate the wit.h runtime header:

      wit-bindgen cpp wit --world consumer --out-dir build/wit-h
      cp build/wit-h/wit.h src/provider-Cpp/wit.h
    2. Generate the bindings:

      wit-bindgen cpp wit --world provider --out-dir src/provider-Cpp
      wit-bindgen c   wit --world consumer --out-dir src/consumer-C
    3. Compile the C++ provider component:

      mkdir -p components
      wasm32-wasip2-clang++ -std=c++23 -fno-exceptions -mexec-model=reactor \
        src/provider-Cpp/provider.cpp src/provider-Cpp/provider_impl.cpp \
        src/provider-Cpp/provider_component_type.o \
        -I src/provider-Cpp -o components/provider.wasm
    4. Compile the C consumer component:

      wasm32-wasip2-clang -mexec-model=reactor \
        src/consumer-C/consumer.c src/consumer-C/consumer_impl.c \
        src/consumer-C/consumer_component_type.o \
        -I src/consumer-C -o components/consumer.wasm
    5. Compose the two components:

      wac plug components/consumer.wasm --plug components/provider.wasm -o composed.wasm
    6. Run the composed component:

      wasmtime run composed.wasm
      jco run composed.wasm

    Expected result

    Both runtimes print exactly the following and exit 0:

    "temres26"
    

    Actual result

    jco 1.16.1: rc=0, stdout:

    ""
    

    wasmtime 41.0.0, same file: rc=1, no stdout, stderr:

    Error: failed to run main module `composed.wasm`
    
    Caused by:
        0: failed to invoke `run` function
        1: error while executing at wasm backtrace:
               0:     0xe9 - <unknown>!<wasm function 3>
               1:  0x46974 - consumer.wasm!mydefined_min_itf_f
               2:  0x46b05 - consumer.wasm!exports_wasi_cli_run_run
               3:  0x469cf - consumer.wasm!__wasm_export_exports_wasi_cli_run_run
           note: using the `WASMTIME_BACKTRACE_DETAILS=1` environment variable may show more debugging information
        2: unknown handle index 4294967295
    
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions