clockworklabs/SpacetimeDB · critical
ERROR: HTTP handler must be registered before it is referenc
Error message
ERROR: HTTP handler must be registered before it is referenced by a router
What it means
LookupHttpHandlerName() scans g_http_handlers (filled by RegisterHttpHandlerHandler) for the handler symbol a Router route references; when no entry matches it prints this message and calls std::abort() during module preinit. HTTP handler registration must execute before the router that references it, because ordering is driven by the numeric prefixes of the generated __preinit__NN_* export functions.
Source
Thrown at crates/bindings-cpp/src/internal/Module.cpp:132
// Register a procedure handler (called by V9Builder during registration)
void RegisterProcedureHandler(const std::string& name,
std::function<std::vector<uint8_t>(ProcedureContext&, BytesSource)> handler) {
g_procedure_handlers.push_back({name, handler});
}
void RegisterHttpHandlerHandler(const std::string& name,
HttpHandlerSymbol handler_symbol,
std::function<HttpResponse(HandlerContext&, HttpRequest)> handler) {
g_http_handlers.push_back({name, handler_symbol, handler});
}
std::string LookupHttpHandlerName(HttpHandlerSymbol handler_symbol) {
auto it = std::find_if(g_http_handlers.begin(), g_http_handlers.end(), [&](const auto& existing) {
return existing.symbol == handler_symbol;
});
if (it == g_http_handlers.end()) {
fprintf(stderr, "ERROR: HTTP handler must be registered before it is referenced by a router\n");
std::abort();
}
return it->name;
}
// Get the number of registered view handlers
size_t GetViewHandlerCount() {
return g_view_handlers.size();
}
size_t GetAnonymousViewHandlerCount() {
return g_view_anon_handlers.size();
}
// Get the number of registered procedure handlers
size_t GetProcedureHandlerCount() {
return g_procedure_handlers.size();
}View on GitHub (pinned to 6dee26c6ef)
Solutions
- Move the HTTP handler registration macro so it is registered before the router references it (earlier in the same translation unit, or via a smaller __preinit__ sequence number)
- Declare the handler and its registration in the same header that defines the router, so include order enforces registration order
- Verify the handler symbol name used by the route exactly matches the registered handler symbol
Example fix
// before: router preinit runs before handler registration ROUTER(my_router); // references get_user handler HTTP_HANDLER(get_user); // registered too late -> abort // after HTTP_HANDLER(get_user); // register handler first ROUTER(my_router); // then reference it
Defensive patterns
Strategy: validation
Validate before calling
# verify preinit ordering in the built module before publishing # nm <module.wasm> | grep __preinit | sort # handler registration exports must sort before the router registration export nm my_module.wasm | grep '__preinit' | sort
Prevention
- Define HTTP handlers and their registration macros in the same header the router includes
- Place handler registration textually before router construction in every translation unit
- Run the nm-based ordering check in CI for modules using the HTTP feature
When it happens
Trigger: A route references a handler whose HTTP_HANDLER registration macro expands to a preinit function that runs after the router's preinit; handler and router split across translation units with unfavorable preinit numbering; the handler symbol referenced by the route differs from the one actually registered.
Common situations: Moving handler macros into a separate header/file during refactoring so their __preinit__ ordering lands after the router's; hand-writing router registration instead of using the provided macros that guarantee ordering.
Related errors
- Router registration failed: %s
- ERROR: Invalid http handler ID %u (have %zu handlers)\n
- cannot serialize refs without a typespace
- cannot deserialize refs without a typespace
- Route conflict for `${path}`
AI-assisted analysis of clockworklabs/SpacetimeDB@6dee26c6ef (2026-08-20).
Data as JSON: /api/errors/057d1179324fa430.
Report an issue: GitHub.