{"record":{"id":"2d265c925bd6108f","repo":"clockworklabs/SpacetimeDB","slug":"router-registration-failed-s","errorCode":null,"errorMessage":"Router registration failed: %s\n","messagePattern":"Router registration failed: (.+?)\n","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"crates/bindings-cpp/include/spacetimedb/router.h","lineNumber":99,"sourceCode":"    }\n\n    Router merge(const Router& other) const {\n        Router merged = *this;\n        for (const auto& route : other.routes_) {\n            merged = merged.add_route(route.method, route.path, route.handler_name);\n        }\n        return merged;\n    }\n\n    const std::vector<RouteSpec>& routes() const {\n        return routes_;\n    }\n\nprivate:\n    std::vector<RouteSpec> routes_;\n\n    [[noreturn]] static void fail_router_registration(const std::string& message) {\n        std::fprintf(stderr, \"Router registration failed: %s\\n\", message.c_str());\n        std::abort();\n    }\n\n    static bool character_is_acceptable_for_route_path(char c) {\n        return (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || c == '-' || c == '_' || c == '~' || c == '/';\n    }\n\n    static void assert_valid_path(const std::string& path) {\n        if (!path.empty() && path[0] != '/') {\n            fail_router_registration(\"Route paths must start with `/`: \" + path);\n        }\n        for (char c : path) {\n            if (!character_is_acceptable_for_route_path(c)) {\n                fail_router_registration(\"Route paths may contain only ASCII lowercase letters, digits and `-_~/`: \" + path);\n            }\n        }\n    }\n","sourceCodeStart":81,"sourceCodeEnd":117,"githubUrl":"https://github.com/clockworklabs/SpacetimeDB/blob/524b4487d949b61a07d4f39c862d1290259dfd20/crates/bindings-cpp/include/spacetimedb/router.h#L81-L117","documentation":"The HTTP Router (unstable feature, requires SPACETIMEDB_UNSTABLE_FEATURES) validates every route path at registration time. fail_router_registration prints the specific reason and calls std::abort(), so the process dies during static initialization — before main. Reasons include: a path not starting with '/', a character outside [a-z0-9-_~/] (uppercase and spaces are rejected), nest() into a prefix that existing routes already use, and duplicate method+path pairs.","triggerScenarios":"Calling Router::get(\"users\", h) without a leading slash; paths containing uppercase, spaces, dots, or other characters rejected by character_is_acceptable_for_route_path; Router::nest(\"/api\", sub) when routes already beginning with /api exist; merging two routers that both define the same method and path (routes_overlap).","commonSituations":"Porting REST paths verbatim from another framework that allowed case-insensitive or parameterized paths like '/users/:id' (':' is rejected); composing routers with nest() where prefixes collide; refactoring a route from one method to another while forgetting the old registration; enabling the unstable HTTP feature for the first time on legacy path strings.","solutions":["Read the message text after 'Router registration failed:' — it names the exact rule and offending path","Normalize paths: start with '/', lowercase only, restricted to a-z 0-9 - _ ~ /","For nest() collisions, restructure prefixes so no existing route starts with the nest path, or nest before adding overlapping routes","For duplicates found via routes_overlap, remove or rename one of the same method+path registrations","Re-run: abort happens at static init, so the module process must simply start cleanly"],"exampleFix":"// before — aborts at startup\nauto r = SpacetimeDB::Router{}.get(\"Users\", h)          // no leading '/', uppercase\n                         .get(\"/users/:id\", h2);       // ':' not allowed\n// after\nauto r = SpacetimeDB::Router{}.get(\"/users\", h)\n                         .get(\"/users/~id\", h2);      // valid charset, leading '/'","handlingStrategy":"validation","validationCode":"// Validate before building the Router (abort() cannot be caught):\n#include <cctype>\nbool RoutePathIsValid(const std::string& p) {\n    if (p.empty() || p[0] != '/') return false;\n    for (char c : p) {\n        bool ok = (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9')\n                  || c == '-' || c == '_' || c == '~' || c == '/';\n        if (!ok) return false;\n    }\n    return true;\n}\n// assert RoutePathIsValid(path) for every route in a unit test / startup check","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Lowercase all route paths and prefix them with '/' by construction (build from constants)","Reject uppercase, spaces, ':' and '.' in path strings at the source — the router's charset is [a-z0-9-_~/]","Check nest()/merge() compositions for prefix and method+path overlaps in a unit test, since duplicates abort at static init","Run the module binary in CI before deploy — the abort happens before main, so a smoke start catches bad routes"],"tags":["spacetimedb","cpp","http-router","route-validation","abort","static-initialization","unstable-features"],"backgroundTag":"http-route-validation-failed","analyzedSha":"524b4487d949b61a07d4f39c862d1290259dfd20","analyzedAt":"2026-08-16T23:58:54.611Z","schemaVersion":2},"datasetVersion":"2026-08-17T04:17:16.089Z"}