clockworklabs/SpacetimeDB · error
{CALL_PROCEDURE_DUNDER} export is not a function
Error message
{CALL_PROCEDURE_DUNDER} export is not a function What it means
Host-side panic while instantiating a wasm module: the module exports something named `__call_procedure__` but the export is not a function (it is a memory, table, or global). The SpacetimeDB ABI reserves double-underscore names; `get_call_procedure` looks the export up and requires a function of the exact expected signature. Missing exports are fine (pre-procedure modules), but a wrong-kind export corrupts the ABI, so instantiation panics. New modules get this validated at publish time; old or hand-built modules can slip through.
Source
Thrown at crates/core/src/host/wasmtime/wasmtime_module.rs:455
/// which is fine because they also won't define any procedures.
///
/// Panics if the `instance` has an export at the expected name,
/// but it is not a function or is a function of an inappropriate type.
/// For new modules, this will be caught during publish.
/// Old modules from before the introduction of procedures might have an export at that name,
/// but it follows the double-underscore pattern of reserved names,
/// so we're fine to break those modules.
fn get_call_procedure(store: &mut Store<WasmInstanceEnv>, instance: &Instance) -> Option<CallProcedureType> {
// Wasmtime uses `anyhow` for error reporting, vexing library users the world over.
// This means we can't distinguish between the failure modes of `Instance::get_typed_func`.
// Instead, we type out the body of that method ourselves,
// but with error handling appropriate to our needs.
let export = instance.get_export(store.as_context_mut(), CALL_PROCEDURE_DUNDER)?;
Some(
export
.into_func()
.unwrap_or_else(|| panic!("{CALL_PROCEDURE_DUNDER} export is not a function"))
.typed(store)
.unwrap_or_else(|err| panic!("{CALL_PROCEDURE_DUNDER} export is a function with incorrect type: {err}")),
)
}
/// Look up the `instance`'s export named by [`CALL_VIEW_DUNDER`].
///
/// Similar to [`get_call_procedure`], but for views.
fn get_call_view(store: &mut Store<WasmInstanceEnv>, instance: &Instance) -> Option<CallViewType> {
let export = instance.get_export(store.as_context_mut(), CALL_VIEW_DUNDER)?;
Some(
export
.into_func()
.unwrap_or_else(|| panic!("{CALL_VIEW_DUNDER} export is not a function"))
.typed(store)
.unwrap_or_else(|err| panic!("{CALL_VIEW_DUNDER} export is a function with incorrect type: {err}")),
)
}View on GitHub (pinned to 6dee26c6ef)
Solutions
- Rebuild the module with the official SpacetimeDB SDK/CLI (`spacetime publish`) so the dunder exports are generated correctly.
- Inspect the artifact before publishing: `wasm-objdump -x module.wasm | grep -A2 __call_procedure__` (or `wasm-tools print`) and confirm the Export is a `func`.
- Remove any custom exports/renames that collide with reserved double-underscore names in your build pipeline.
- If the module was built by the SDK and still misbehaves, report the toolchain versions with the wasm export dump.
Example fix
; before (WAT): export is not a function (global (export "__call_procedure__") i32 (i32.const 0)) ; after (func (export "__call_procedure__") (param i32) (result i32) ;; dispatch to procedures as generated by the SDK )
Defensive patterns
Strategy: type-guard
Validate before calling
# Inspect the artifact before publishing: wasm-objdump -x module.wasm | grep -A1 '__call_procedure__' # The Export must show type: func (not global/table/memory).
Type guard
// Host-side narrowing: only treat dunder exports as callables when the
// export kind is Func, and skip/error otherwise instead of panicking.
fn get_call_procedure_safe(
store: &mut Store<WasmInstanceEnv>,
instance: &Instance,
) -> Option<CallProcedureType> {
let export = instance.get_export(store.as_context_mut(), CALL_PROCEDURE_DUNDER)?;
let func = export.into_func()?; // None instead of panic for non-func exports
func.typed(store).ok()
} Prevention
- Always build modules through the SpacetimeDB SDK/CLI so dunder exports are emitted correctly.
- Add a CI step that fails when reserved `__call_*__` exports exist with non-func kinds.
- Avoid wasm post-processing (renaming/optimization) that can change export kinds.
When it happens
Trigger: Publishing/wasm-instantiating a module where `__call_procedure__` was exported as a global/table/memory: hand-written WAT/wasm, custom build pipelines that emit extra exports, name collisions from obfuscators or linkers, or modules built with mismatched toolchain versions.
Common situations: Building modules outside the official SDK (raw cargo/rustc targets without spacetimedb bindings); wasm post-processing tools (optimizers, symbol renaming) turning the export into a different entity; upgrading spacetimedb where new dunder exports were introduced while an old artifact is re-uploaded.
Related errors
- {CALL_PROCEDURE_DUNDER} export is a function with incorrect
- {CALL_VIEW_DUNDER} export is not a function
- {CALL_VIEW_DUNDER} export is a function with incorrect type:
- {CALL_VIEW_ANON_DUNDER} export is not a function
- {CALL_VIEW_ANON_DUNDER} export is a function with incorrect
AI-assisted analysis of clockworklabs/SpacetimeDB@6dee26c6ef (2026-08-20).
Data as JSON: /api/errors/5fa763e91070c6d1.
Report an issue: GitHub.