clockworklabs/SpacetimeDB · error

Invalid HTTP method

Error message

Invalid HTTP method

What it means

Panic in SpacetimeDB's host module environment when a SpacetimeDB module performs an outbound HTTP request. The module-facing `st_http::Method` enum includes an `Extension(String)` variant; extension methods are converted with `http::Method::from_bytes(...).expect("Invalid HTTP method")`. If the string is not a valid HTTP method token per RFC 7230 (only visible ASCII, no separators/control chars, non-empty), the conversion fails and the module's host call panics.

Solutions

  1. Sanitize or validate the method string before calling the HTTP host API: require non-empty ASCII token chars only (tchar set: `!#$%&'*+-.^_`|~`, digits, A-Z, a-z).
  2. Reject or fall back to a safe default (e.g. `GET`) when the method doesn't match `^[!#$%&'*+\-.^_`|~0-9A-Za-z]+$`.
  3. If you intended a standard verb, use the typed variant (e.g. `Method::Post`) instead of building an extension string.
  4. Upgrade the module SDK once HTTP host calls return typed errors instead of panicking for invalid methods.

Example fix

// before: passing raw user input as an HTTP method
let method = st_http::Method::Extension(user_input.clone());
client.request(method, uri, ...);

// after: validate the token before calling
fn valid_method_token(s: &str) -> bool {
    !s.is_empty() && s.bytes().all(|b| b.is_ascii_graphic() && !b"()<>@,;:\\"/[]?={}".contains(&b))
}
let method = if valid_method_token(&user_input) {
    st_http::Method::Extension(user_input)
} else { return Err(BadRequest::invalid_method(&user_input)); };
client.request(method, uri, ...);
Defensive patterns

Strategy: validation

Validate before calling

fn valid_method_token(s: &str) -> bool {
    !s.is_empty()
        && s.bytes().all(|b| {
            b.is_ascii_alphanumeric() || b"!#$%&'*+-.^_`|~".contains(&b)
        })
}
// reject before calling the HTTP host API
if !valid_method_token(&method_str) { return Err(InvalidMethod(method_str)); }

Type guard

// Rust: narrow to the safe extension-method domain before the host call
fn safe_method(s: &str) -> Option<st_http::Method> {
    valid_method_token(s).then(|| st_http::Method::Extension(s.to_owned()))
}

Try / catch

let r = std::panic::catch_unwind(AssertUnwindSafe(|| client.request(m, uri, body)));
match r { Ok(res) => res?, Err(_) => return Err(HostCallPanic::invalid_method) }

Prevention

When it happens

Trigger: A module calls `HttpClient::request` (or similar host HTTP API) with `Method::Extension(method)` where `method` contains invalid token characters — spaces, unicode, control bytes, delimiters like `()<>@,;:\"/[]?={}`, or an empty string. Standard methods (GET/POST/…) map to constants and never hit this path.

Common situations: User-supplied or config-driven method strings passed verbatim into HTTP requests (e.g. treating method as free-form input); WebDAV/custom methods constructed by string concatenation that accidentally include whitespace or newlines; copy-pasting methods like `"GET "` with trailing space; generated clients forwarding arbitrary client input.

Related errors


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

Appendix: source

Thrown at crates/core/src/host/instance_env.rs:1329

        method,
        headers,
        timeout,
        uri,
        version,
    } = request;

    let (mut request, ()) = http::Request::new(()).into_parts();
    request.method = match method {
        st_http::Method::Get => http::Method::GET,
        st_http::Method::Head => http::Method::HEAD,
        st_http::Method::Post => http::Method::POST,
        st_http::Method::Put => http::Method::PUT,
        st_http::Method::Delete => http::Method::DELETE,
        st_http::Method::Connect => http::Method::CONNECT,
        st_http::Method::Options => http::Method::OPTIONS,
        st_http::Method::Trace => http::Method::TRACE,
        st_http::Method::Patch => http::Method::PATCH,
        st_http::Method::Extension(method) => http::Method::from_bytes(method.as_bytes()).expect("Invalid HTTP method"),
    };
    // The error type here, `http::uri::InvalidUri`, doesn't contain the URI itself,
    // so it's safe to return and to log.
    // See https://docs.rs/http/1.3.1/src/http/uri/mod.rs.html#120-141 .
    request.uri = uri.try_into()?;
    request.version = match version {
        st_http::Version::Http09 => http::Version::HTTP_09,
        st_http::Version::Http10 => http::Version::HTTP_10,
        st_http::Version::Http11 => http::Version::HTTP_11,
        st_http::Version::Http2 => http::Version::HTTP_2,
        st_http::Version::Http3 => http::Version::HTTP_3,
    };
    request.headers = headers
        .into_iter()
        .map(|(k, v)| {
            Ok((
                // The error type here, `http::header::InvalidHeaderName`, doesn't contain the header name itself,
                // so it's safe to return and to log.

View on GitHub (pinned to eddf9f5014)