clockworklabs/SpacetimeDB · error
NO_SUCH_PROCEDURE
NO_SUCH_PROCEDURE
Error message
ERROR: Invalid procedure ID %u (have %zu procedures)\n
What it means
Dispatch guard in Module::__call_procedure__: the host called a procedure by numeric ID >= g_procedure_handlers.size(). The function logs the message and returns -1 (NO_SUCH_PROCEDURE). The procedure handler table registered at init does not cover the ID the server derived from the published module description.
Source
Thrown at crates/bindings-cpp/src/internal/Module.cpp:591
// Call the view handler - returns fully serialized result data, including the header byte.
std::vector<uint8_t> result_data = handler_info.handler(ctx, args_source);
WriteBytes(result_sink, result_data);
return 2; // Success with data
}
// Dispatch function for procedures
int16_t Module::__call_procedure__(
uint32_t id,
uint64_t sender_0, uint64_t sender_1, uint64_t sender_2, uint64_t sender_3,
uint64_t conn_id_0, uint64_t conn_id_1,
uint64_t timestamp_microseconds,
BytesSource args_source,
BytesSink result_sink
) {
// Check if procedure ID is valid
if (id >= g_procedure_handlers.size()) {
fprintf(stderr, "ERROR: Invalid procedure ID %u (have %zu procedures)\n",
id, g_procedure_handlers.size());
return -1; // NO_SUCH_PROCEDURE
}
// Create sender identity from the 4 uint64_t parts
std::array<uint8_t, 32> sender_bytes{};
std::memcpy(sender_bytes.data(), &sender_0, 8);
std::memcpy(sender_bytes.data() + 8, &sender_1, 8);
std::memcpy(sender_bytes.data() + 16, &sender_2, 8);
std::memcpy(sender_bytes.data() + 24, &sender_3, 8);
Identity sender_identity(sender_bytes);
// Create timestamp from microseconds
Timestamp timestamp = Timestamp::from_micros_since_epoch(
static_cast<int64_t>(timestamp_microseconds));
// Create connection ID from the two 64-bit parts (full 128-bit value)View on GitHub (pinned to 6dee26c6ef)
Solutions
- Republish the module so procedure IDs and handlers line up
- Keep procedure registration macros unconditional
- Verify server logs show a clean module init (no skipped preinit steps)
- Regenerate client bindings after procedure schema changes
Example fix
// before
#if WITH_PROCEDURES
PROCEDURE(compute) { /* ... */ }
#endif
// after
PROCEDURE(compute) { /* ... */ } // always registered Defensive patterns
Strategy: validation
Prevention
- Register procedures unconditionally across build configurations
- Republish and regenerate bindings whenever procedures change
- Guard CI with a handler-count smoke test like the reducer one
When it happens
Trigger: Procedure registration macros conditionally compiled or skipped during a partially failed init; stale binary vs. server metadata after adding/removing procedures; publishing a module whose description and wasm disagree.
Common situations: Version skew between module builds; refactoring procedures behind feature flags; init errors earlier in preinit leaving fewer handlers registered.
Related errors
- NO_SUCH_REDUCER
- NO_SUCH_VIEW
- ERROR: Invalid http handler ID %u (have %zu handlers)\n
- There is already a reducer, procedure, or view with the name
- Router registration failed: %s
AI-assisted analysis of clockworklabs/SpacetimeDB@6dee26c6ef (2026-08-20).
Data as JSON: /api/errors/7cdac60fdc83abf7.
Report an issue: GitHub.