clockworklabs/SpacetimeDB · error

Route conflict for `{path}`

Error message

Route conflict for `{path}`

What it means

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.

Source

Thrown at crates/bindings/src/http.rs:387

    }

    pub(crate) fn into_routes(self) -> Vec<RouteSpec> {
        self.routes
    }

    fn add_route(mut self, method: MethodOrAny, path: impl Into<String>, handler: Handler) -> Self {
        let path = path.into();
        assert_valid_path(&path);

        let candidate = RouteSpec {
            method: method.clone(),
            path: path.clone(),
            handler,
        };

        // TODO(perf): Adding a route is O(n), which means that building a router is O(n^2)
        if self.routes.iter().any(|route| routes_overlap(route, &candidate)) {
            panic!("Route conflict for `{path}`");
        }

        self.routes.push(candidate);
        self
    }
}

#[cfg(feature = "unstable")]
fn join_paths(prefix: &str, suffix: &str) -> String {
    if prefix == "/" {
        return suffix.to_string();
    }
    if suffix == "/" {
        return prefix.to_string();
    }
    let prefix = prefix.trim_end_matches('/');
    let suffix = suffix.trim_start_matches('/');
    format!("{prefix}/{suffix}")

View on GitHub (pinned to 6dee26c6ef)

Solutions

  1. Delete or rename the duplicate registration - search for the path shown in the panic message.
  2. If a catch-all is intended, keep a single Method::Any handler per path and remove per-method handlers there.
  3. When merging routers, de-duplicate shared paths before merging.

Example fix

// before
let r = Router::new()
    .route(Method::GET, "/users", list)
    .route(Method::GET, "/users", list_again) // panics
    .route(Method::POST, "/users", create); // ok: different method

// after
let r = Router::new()
    .route(Method::GET, "/users", list)
    .route(Method::POST, "/users", create);
Defensive patterns

Strategy: validation

Validate before calling

use std::collections::HashSet;
let mut seen: HashSet<(Method, String)> = HashSet::new();
let key = (method.clone(), path.to_string());
assert!(!seen.contains(&(MethodOrAny::Any, path.to_string())) && !seen.contains(&key), "duplicate route {path}");
seen.insert(key);

Prevention

When it happens

Trigger: 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.

Common situations: Combining Router::merge outputs from multiple modules; copy-pasted route blocks; registering fallback Any handlers alongside per-method handlers.

Related errors


AI-assisted analysis of clockworklabs/SpacetimeDB@6dee26c6ef (2026-08-20). Data as JSON: /api/errors/874ed4d3d50b5234. Report an issue: GitHub.