clockworklabs/SpacetimeDB · critical
{CALL_VIEW_DUNDER} export is not a function
Error message
{CALL_VIEW_DUNDER} export is not a function What it means
When hooking up a module instance, get_call_view resolves the `__call_view__` export (the generated entry point for SQL views over the module). `Export::into_func()` returns None when the export is a memory, global, or table instead of a function, and the host panics immediately. A non-function export carrying that reserved name is treated as a malformed module. The export itself is optional (get_export returning None yields None from the function), so the panic only fires when something occupies the name with the wrong kind.
Source
Thrown at crates/core/src/host/wasmtime/wasmtime_module.rs:469
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}")),
)
}
/// Look up the `instance`'s export named by [`CALL_VIEW_ANON_DUNDER`].
///
/// Similar to [`get_call_procedure`], but for anonymous views.
fn get_call_view_anon(store: &mut Store<WasmInstanceEnv>, instance: &Instance) -> Option<CallViewAnonType> {
let export = instance.get_export(store.as_context_mut(), CALL_VIEW_ANON_DUNDER)?;
Some(
export
.into_func()
.unwrap_or_else(|| panic!("{CALL_VIEW_ANON_DUNDER} export is not a function"))
.typed(store)
.unwrap_or_else(|err| panic!("{CALL_VIEW_ANON_DUNDER} export is a function with incorrect type: {err}")),
)
}View on GitHub (pinned to 6dee26c6ef)
Solutions
- Find the offending export: `wasm-objdump -x module.wasm | grep -A2 __call_view__` shows whether it is a global/memory/table.
- Rename or remove the conflicting export in module source (check any #[export_name] attributes) and rebuild.
- Rebuild with the SDK matching the server so codegen emits the function export correctly.
- Reserve the `__call_*__` dunder names for generated code only.
Example fix
// before: accidental export-kind collision with the reserved name #[export_name = "__call_view__"] static VIEW_FLAG: AtomicBool = AtomicBool::new(false); // after: do not shadow the reserved dunder export name static VIEW_FLAG: AtomicBool = AtomicBool::new(false);
Defensive patterns
Strategy: type-guard
Validate before calling
use wasmtime::{Engine, Module, ExternType};
fn no_dunder_name_collisions(engine: &Engine, wasm: &[u8]) -> Result<(), String> {
let module = Module::new(engine, wasm).map_err(|e| e.to_string())?;
for name in ["__call_procedure__", "__call_view__", "__call_view_anon__", "__call_http_handler__"] {
if let Some(exp) = module.get_export(name) {
match exp.ty() {
ExternType::Func(_) => {}
other => return Err(format!("{name} exported as {other:?}, expected a function")),
}
}
}
Ok(())
} Type guard
fn is_func_export(ty: &ExternType) -> bool { matches!(ty, ExternType::Func(_)) } Try / catch
let r = std::panic::catch_unwind(|| host.instantiate(module));
if let Err(p) = r {
if panic_message(&p).contains("__call_view__ export is not a function") {
// a non-function item holds the reserved name: fix module exports and rebuild
}
} Prevention
- Treat the `__call_*__` names as reserved in lint rules for module code.
- Audit #[export_name] attributes before publishing.
- Run an export-kind check in CI on built wasm artifacts.
When it happens
Trigger: Instantiating a module that exports `__call_view__` as a global, memory, or table rather than a wasm function; hit inside get_call_view's `into_func().unwrap_or_else(panic!)` during publish or database start.
Common situations: Name collision: a static, memory, or custom export accidentally named `__call_view__` (e.g. via #[export_name]); hand-written WAT exporting a global with the reserved name; codegen changes between SDK versions exporting the wrong kind.
Related errors
- {CALL_VIEW_ANON_DUNDER} export is not a function
- {CALL_HTTP_HANDLER_DUNDER} export is not a function
- {CALL_PROCEDURE_DUNDER} export is not a function
- {CALL_PROCEDURE_DUNDER} export is a function with incorrect
- {CALL_VIEW_DUNDER} export is a function with incorrect type:
AI-assisted analysis of clockworklabs/SpacetimeDB@6dee26c6ef (2026-08-20).
Data as JSON: /api/errors/88d4ca8c20bf1ec6.
Report an issue: GitHub.