{"record":{"id":"cb4e49f365a25d0e","repo":"clockworklabs/SpacetimeDB","slug":"invalid-http-method","errorCode":null,"errorMessage":"Invalid HTTP method","messagePattern":"Invalid HTTP method","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/core/src/host/instance_env.rs","lineNumber":1329,"sourceCode":"        method,\n        headers,\n        timeout,\n        uri,\n        version,\n    } = request;\n\n    let (mut request, ()) = http::Request::new(()).into_parts();\n    request.method = match method {\n        st_http::Method::Get => http::Method::GET,\n        st_http::Method::Head => http::Method::HEAD,\n        st_http::Method::Post => http::Method::POST,\n        st_http::Method::Put => http::Method::PUT,\n        st_http::Method::Delete => http::Method::DELETE,\n        st_http::Method::Connect => http::Method::CONNECT,\n        st_http::Method::Options => http::Method::OPTIONS,\n        st_http::Method::Trace => http::Method::TRACE,\n        st_http::Method::Patch => http::Method::PATCH,\n        st_http::Method::Extension(method) => http::Method::from_bytes(method.as_bytes()).expect(\"Invalid HTTP method\"),\n    };\n    // The error type here, `http::uri::InvalidUri`, doesn't contain the URI itself,\n    // so it's safe to return and to log.\n    // See https://docs.rs/http/1.3.1/src/http/uri/mod.rs.html#120-141 .\n    request.uri = uri.try_into()?;\n    request.version = match version {\n        st_http::Version::Http09 => http::Version::HTTP_09,\n        st_http::Version::Http10 => http::Version::HTTP_10,\n        st_http::Version::Http11 => http::Version::HTTP_11,\n        st_http::Version::Http2 => http::Version::HTTP_2,\n        st_http::Version::Http3 => http::Version::HTTP_3,\n    };\n    request.headers = headers\n        .into_iter()\n        .map(|(k, v)| {\n            Ok((\n                // The error type here, `http::header::InvalidHeaderName`, doesn't contain the header name itself,\n                // so it's safe to return and to log.","sourceCodeStart":1311,"sourceCodeEnd":1347,"githubUrl":"https://github.com/clockworklabs/SpacetimeDB/blob/eddf9f5014579a50d4b67630e28b6e15cad9c4af/crates/core/src/host/instance_env.rs#L1311-L1347","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["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).","Reject or fall back to a safe default (e.g. `GET`) when the method doesn't match `^[!#$%&'*+\\-.^_`|~0-9A-Za-z]+$`.","If you intended a standard verb, use the typed variant (e.g. `Method::Post`) instead of building an extension string.","Upgrade the module SDK once HTTP host calls return typed errors instead of panicking for invalid methods."],"exampleFix":"// before: passing raw user input as an HTTP method\nlet method = st_http::Method::Extension(user_input.clone());\nclient.request(method, uri, ...);\n\n// after: validate the token before calling\nfn valid_method_token(s: &str) -> bool {\n    !s.is_empty() && s.bytes().all(|b| b.is_ascii_graphic() && !b\"()<>@,;:\\\\\"/[]?={}\".contains(&b))\n}\nlet method = if valid_method_token(&user_input) {\n    st_http::Method::Extension(user_input)\n} else { return Err(BadRequest::invalid_method(&user_input)); };\nclient.request(method, uri, ...);","handlingStrategy":"validation","validationCode":"fn valid_method_token(s: &str) -> bool {\n    !s.is_empty()\n        && s.bytes().all(|b| {\n            b.is_ascii_alphanumeric() || b\"!#$%&'*+-.^_`|~\".contains(&b)\n        })\n}\n// reject before calling the HTTP host API\nif !valid_method_token(&method_str) { return Err(InvalidMethod(method_str)); }","typeGuard":"// Rust: narrow to the safe extension-method domain before the host call\nfn safe_method(s: &str) -> Option<st_http::Method> {\n    valid_method_token(s).then(|| st_http::Method::Extension(s.to_owned()))\n}","tryCatchPattern":"let r = std::panic::catch_unwind(AssertUnwindSafe(|| client.request(m, uri, body)));\nmatch r { Ok(res) => res?, Err(_) => return Err(HostCallPanic::invalid_method) }","preventionTips":["Never forward raw user/config input as an HTTP method; validate the RFC 7230 token charset first.","Use typed standard methods (Method::Get, Method::Post) whenever the verb is known.","Reject early at the module API boundary with a descriptive error instead of letting the host panic."],"tags":["rust","spacetimedb","http","module","validation","panic","user-input"],"backgroundTag":"invalid-http-method","analyzedSha":"eddf9f5014579a50d4b67630e28b6e15cad9c4af","analyzedAt":"2026-08-20T06:08:37.179Z","contentChangedAt":"2026-08-20T06:08:37.179Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}