clockworklabs/SpacetimeDB · error

Route paths may contain only {}: {path}

Error message

Route paths may contain only {}: {path}

What it means

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}".

Source

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

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}")
}

#[cfg(feature = "unstable")]
fn assert_valid_path(path: &str) {
    if !path.is_empty() && !path.starts_with('/') {
        panic!("Route paths must start with `/`: {path}");
    }
    if !path.chars().all(character_is_acceptable_for_route_path) {
        panic!(
            "Route paths may contain only {}: {path}",
            ACCEPTABLE_ROUTE_PATH_CHARS_HUMAN_DESCRIPTION
        );
    }
}

#[cfg(feature = "unstable")]
fn routes_overlap(a: &RouteSpec, b: &RouteSpec) -> bool {
    if a.path != b.path {
        return false;
    }
    matches!(a.method, MethodOrAny::Any) || matches!(b.method, MethodOrAny::Any) || a.method == b.method
}

/// Allows performing HTTP requests via [`HttpClient::send`] and [`HttpClient::get`].
///
/// Access an `HttpClient` from within [procedures](crate::procedure)
/// via [the `http` field of the `ProcedureContext`](crate::ProcedureContext::http).

View on GitHub (pinned to 6dee26c6ef)

Solutions

  1. Lowercase the path and strip unsupported characters; use only a-z, 0-9, '-', '_', '/', '~'.
  2. Replace path parameters with query parameters (e.g. "/user?id=42") since dynamic segments are not supported.
  3. Replace dots with '-' (e.g. "/report-json") and pass extensions as query args.

Example fix

// before
let r = Router::new()
    .route(Method::GET, "/Users/{id}/Orders.json", h); // panics: '{', '.', uppercase

// after: static lowercase paths, dynamic data via query params
let r = Router::new()
    .route(Method::GET, "/user-orders", h); // read id/format from the request query
Defensive patterns

Strategy: validation

Validate before calling

fn valid_route_path(p: &str) -> bool {
    !p.is_empty()
        && p.starts_with('/')
        && p.chars().all(|c| c.is_ascii_lowercase() || c.is_ascii_digit() || matches!(c, '-' | '_' | '~' | '/'))
}
assert!(valid_route_path("/user-orders"));

Prevention

When it happens

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

Common situations: 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.

Related errors


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