Skip to content

Troubleshooting

[ahoi] bridge ABI mismatch: the wasm module speaks ABI v2,
but this JS bridge expects v1.
Align the `ahoi` crate and npm package versions.

The ahoi crate and @acheul/ahoi-js are from different releases.

Upgrade both together. If you pin one version, pin both.

panicked at src/lib.rs:265:39:
called `Result::unwrap()` on an `Err` value: BorrowConflict

A guard on the same root stock was still alive when another one was taken, and at least one of them was a write.

// wrong: the read guard lives to the end of the statement
state.count().set(*state.count().read() + 1);
// right: the guard is dropped before anything else runs
let next = {
let mut c = state.count().write();
*c += 1;
*c
};

The same applies across different fields of one stock, which is the version that surprises people. Borrows are tracked per root, not per path:

// wrong: disjoint fields, but one root and overlapping guards
let value = state.value().read();
*state.runs().write() += 1;
// right
let value = *state.value().read();
*state.runs().write() += value as u32;

A memo that writes a counter while reading other state is the usual way this shows up. Give the counter its own Stock and the conflict cannot happen.

In a debug build the line number is yours, not one inside ahoi. Release builds compile that tracking out, so diagnose this with --dev.

Remember a wasm panic aborts the module: reload the page after fixing.

Hails get an initial value but never update

Section titled “Hails get an initial value but never update”

set_js_hail_dispatcher() is missing from your root pier.

fn run_pier(key: Pier) {
match key {
Pier::Top => {
set_js_hail_dispatcher(); // this
provide_context(Stock::new(State::default()));
}
}
}

Without it, Rust has nowhere to push values, so the first read works and nothing after it does.

Wasm cannot hot-reload. Rebuild, then make sure the page fully reloads:

Terminal window
wasm-pack build --target web --dev
if (import.meta.hot) import.meta.hot.accept(() => import.meta.hot!.invalidate());

Run cargo test. That is what regenerates bindings/: both your exporter’s types and ahoi’s ret maps.

Nothing else triggers it, and wasm-pack build will happily build against the old bindings.

Key enums must use serde’s default, externally tagged representation. Renaming variants or changing the representation is rejected at compile time.

Change the variant name in Rust instead of renaming it in serde.

HashMap<K, V> arrives as a JavaScript Map, not a plain object.

const counts = pier.readHail("FruitCounts"); // Map<string, number>
counts.get("apple"); // right
counts["apple"]; // undefined

There is no PierProvider above the component, or in Svelte no providePier was called in an ancestor.

In React, remember the provider renders children only after its sphere exists; a component rendered outside the provider will not find one.

The pier prop is read once during setup. Re-mount the provider to switch:

<PierProvider :key="currentPier" :pier="currentPier">
<Panel />
</PierProvider>

providePier, useHail, useReadHail, and useTell use setContext, getContext, and onDestroy, so they only work during component initialisation.

Capture them at the top of <script>:

<script lang="ts">
const tell = useTell(); // here
function onClick() {
tell("Increase"); // not here
}
</script>

The ret map is not reaching createAhoi. Check that you passed all five type parameters:

createAhoi<Pier, Hail, Tell, HailRets, TellRets>({ /* ... */ });

A missing HailRets falls back to unknown for hails, and a missing TellRets to undefined for tells.

The path does not exist: an index past the end, a missing map key, a field of an inactive enum variant.

That is by design: writes to an absent path are ignored rather than panicking. The returned Option tells you when you need to know.

if state.items().get(10).set(1).is_none() {
// there is no item 10
}