{"record":{"id":"874ed4d3d50b5234","repo":"clockworklabs/SpacetimeDB","slug":"route-conflict-for-path-874ed4","errorCode":null,"errorMessage":"Route conflict for `{path}`","messagePattern":"Route conflict for `(.+?)`","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/bindings/src/http.rs","lineNumber":387,"sourceCode":"    }\n\n    pub(crate) fn into_routes(self) -> Vec<RouteSpec> {\n        self.routes\n    }\n\n    fn add_route(mut self, method: MethodOrAny, path: impl Into<String>, handler: Handler) -> Self {\n        let path = path.into();\n        assert_valid_path(&path);\n\n        let candidate = RouteSpec {\n            method: method.clone(),\n            path: path.clone(),\n            handler,\n        };\n\n        // TODO(perf): Adding a route is O(n), which means that building a router is O(n^2)\n        if self.routes.iter().any(|route| routes_overlap(route, &candidate)) {\n            panic!(\"Route conflict for `{path}`\");\n        }\n\n        self.routes.push(candidate);\n        self\n    }\n}\n\n#[cfg(feature = \"unstable\")]\nfn 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}\")","sourceCodeStart":369,"sourceCodeEnd":405,"githubUrl":"https://github.com/clockworklabs/SpacetimeDB/blob/6dee26c6efc2856793e12b148a59742964f5d783/crates/bindings/src/http.rs#L369-L405","documentation":"add_route panics when a candidate route overlaps an existing one: the paths are identical AND either handler uses Method::Any or both use the same method. Registering different methods on the same path is legal; this panic means a true duplicate or an Any collision.","triggerScenarios":"Two .route(Method::GET, \"/users\", ...) registrations (often from merged routers); .route(Method::Any, \"/users\", ...) after a specific-method route on \"/users\"; nest() merging a sub-router that contains a path already added on the parent.","commonSituations":"Combining Router::merge outputs from multiple modules; copy-pasted route blocks; registering fallback Any handlers alongside per-method handlers.","solutions":["Delete or rename the duplicate registration - search for the path shown in the panic message.","If a catch-all is intended, keep a single Method::Any handler per path and remove per-method handlers there.","When merging routers, de-duplicate shared paths before merging."],"exampleFix":"// before\nlet r = Router::new()\n    .route(Method::GET, \"/users\", list)\n    .route(Method::GET, \"/users\", list_again) // panics\n    .route(Method::POST, \"/users\", create); // ok: different method\n\n// after\nlet r = Router::new()\n    .route(Method::GET, \"/users\", list)\n    .route(Method::POST, \"/users\", create);","handlingStrategy":"validation","validationCode":"use std::collections::HashSet;\nlet mut seen: HashSet<(Method, String)> = HashSet::new();\nlet key = (method.clone(), path.to_string());\nassert!(!seen.contains(&(MethodOrAny::Any, path.to_string())) && !seen.contains(&key), \"duplicate route {path}\");\nseen.insert(key);","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Register each path+method exactly once; keep route registration in a single table or builder.","Remember Method::Any collides with every specific method on the same path.","When merging routers, de-duplicate shared paths first."],"tags":["rust","http","router","route-conflict","duplicate-route"],"backgroundTag":"route-conflict","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"}