Skip to content

kilnd: E03ED 'Failed to load module' on witness-instrumented cores (real-app scale) — WASI itself works #391

Description

@avrabe

Reproduced the full witness MC/DC path against kilnd (WIP build, 2026-07-08) and isolated the blocker. It is not a WASI or execution bug — WASI runs fine — it's the loader/decoder choking on what witness instrumentation adds at real-app scale.

Repro

wasi component (bin → wasm32-wasip2, 13 wasi imports)
  → meld fuse            → single core (28 globals, 65 KB) — WASI imports inlined
  → witness instrument   → core (1413 branches, 4267 globals, 2 memories, 258 KB)
  → kilnd WITNESS_MODULE=… WITNESS_OUTPUT=…

kilnd fails at load:

✓ WASI enabled (Preview2)  ✓ Component model enabled
is_component=false → execute_traditional_module → Memory OK → Engine OK
→ WASI dispatcher connected → ✗ [Runtime][E03ED] Failed to load module

Isolation (the useful part)

artifact globals memories size kilnd
un-instrumented fused WASI core 28 1 65 KB runs — "execution completed successfully"
synthetic: 9000 globals + _start 9000 0 116 KB ✅ runs
synthetic: 2 memory declarations 0 2 tiny ✅ loads (reaches "no entry point")
witness-instrumented fused core 4267 2 258 KB E03ED at load

So, ruled out: WASI (un-instrumented WASI core runs), witness output (wasm-tools validate accepts it), raw global count (9000 OK), module size (116 KB OK), and multi-memory declaration count (2 OK).

Leading candidate (couldn't fully isolate — wasm-tools' text parser fights the multi-memory-store WAT)

The one structural thing the instrumented core adds that I could not rule out: witness's per-row instrumentation exports a second memory (__witness_trace, memory index 1) and helper functions (__witness_trace_record / _reset / _row_marker) that read/write memory index 1. A 2-memory declaration loads, but using index 1 is the untested delta. Strong suspicion: multi-memory use (non-zero memidx in load/store, or trace-memory instantiation) isn't handled at load/instantiation.

Other un-ruled-out possibilities the decoder owner can check faster than me: passive data/elem from fusion, the added exported functions, or a per-branch construct at count.

Why it matters

This is the single blocker for meld fuse → witness instrument → witness run --harness kilnd on real WASI components (the intended MC/DC path). kiln#343's example "worked" only because it was ~2 globals / 1 memory. Repro module saved at /private/tmp/wasimc-inst.wasm (258 KB); happy to attach or regenerate. Filed from the witness side; this looks like it lives in the kiln-decoder / memory-system area you're already in.

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