{"record":{"id":"d3364aa69a8c0255","repo":"clockworklabs/SpacetimeDB","slug":"route-paths-may-contain-only-path","errorCode":null,"errorMessage":"Route paths may contain only {}: {path}","messagePattern":"Route paths may contain only (.+?): (.+?)","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/bindings/src/http.rs","lineNumber":414,"sourceCode":"fn join_paths(prefix: &str, suffix: &str) -> String {\n    if prefix == \"/\" {\n        return suffix.to_string();\n    }\n    if suffix == \"/\" {\n        return prefix.to_string();\n    }\n    let prefix = prefix.trim_end_matches('/');\n    let suffix = suffix.trim_start_matches('/');\n    format!(\"{prefix}/{suffix}\")\n}\n\n#[cfg(feature = \"unstable\")]\nfn assert_valid_path(path: &str) {\n    if !path.is_empty() && !path.starts_with('/') {\n        panic!(\"Route paths must start with `/`: {path}\");\n    }\n    if !path.chars().all(character_is_acceptable_for_route_path) {\n        panic!(\n            \"Route paths may contain only {}: {path}\",\n            ACCEPTABLE_ROUTE_PATH_CHARS_HUMAN_DESCRIPTION\n        );\n    }\n}\n\n#[cfg(feature = \"unstable\")]\nfn routes_overlap(a: &RouteSpec, b: &RouteSpec) -> bool {\n    if a.path != b.path {\n        return false;\n    }\n    matches!(a.method, MethodOrAny::Any) || matches!(b.method, MethodOrAny::Any) || a.method == b.method\n}\n\n/// Allows performing HTTP requests via [`HttpClient::send`] and [`HttpClient::get`].\n///\n/// Access an `HttpClient` from within [procedures](crate::procedure)\n/// via [the `http` field of the `ProcedureContext`](crate::ProcedureContext::http).","sourceCodeStart":396,"sourceCodeEnd":432,"githubUrl":"https://github.com/clockworklabs/SpacetimeDB/blob/6dee26c6efc2856793e12b148a59742964f5d783/crates/bindings/src/http.rs#L396-L432","documentation":"assert_valid_path rejects route paths containing characters outside the allowed set, which the SDK describes as: ASCII lowercase letters, digits and `-_~/`. Uppercase letters, dots, colons, braces, spaces and any non-ASCII character panic at router construction. Notably this rules out path parameters like \"/users/{id}\".","triggerScenarios":"Using path-parameter syntax (\"/users/{id}\" or \"/users/:id\") which the router does not support; uppercase path segments (\"/Users\"); file-extension style paths (\"/report.json\"); query strings accidentally embedded in the path (\"/x?a=b\").","commonSituations":"Porting REST routes from axum/actix that use path params; kebab-case typos introducing dots or uppercase; assuming URL-encoding is permitted in route strings.","solutions":["Lowercase the path and strip unsupported characters; use only a-z, 0-9, '-', '_', '/', '~'.","Replace path parameters with query parameters (e.g. \"/user?id=42\") since dynamic segments are not supported.","Replace dots with '-' (e.g. \"/report-json\") and pass extensions as query args."],"exampleFix":"// before\nlet r = Router::new()\n    .route(Method::GET, \"/Users/{id}/Orders.json\", h); // panics: '{', '.', uppercase\n\n// after: static lowercase paths, dynamic data via query params\nlet r = Router::new()\n    .route(Method::GET, \"/user-orders\", h); // read id/format from the request query","handlingStrategy":"validation","validationCode":"fn valid_route_path(p: &str) -> bool {\n    !p.is_empty()\n        && p.starts_with('/')\n        && p.chars().all(|c| c.is_ascii_lowercase() || c.is_ascii_digit() || matches!(c, '-' | '_' | '~' | '/'))\n}\nassert!(valid_route_path(\"/user-orders\"));","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Use only a-z, 0-9, '-', '_', '~', '/' in route paths.","No path parameters: pass dynamic values as query parameters instead.","Add a unit test that validates every route string in your router builder."],"tags":["rust","http","router","path-validation","route-params"],"backgroundTag":"invalid-route-path","analyzedSha":"6dee26c6efc2856793e12b148a59742964f5d783","analyzedAt":"2026-08-20T06:08:37.179Z","contentChangedAt":"2026-08-20T06:08:37.179Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}