seanmonstar/warp · error
method_not_allowed
Error message
method_not_allowed
What it means
This is warp's rejection produced when a route matched by path does not accept the HTTP method of the incoming request. The method filter (src/filters/method.rs:138) compares the request's method against the method bound via get()/post()/put()/delete()/head()/options() and rejects with method_not_allowed when they differ. It is the framework's way of signalling HTTP 405 Method Not Allowed.
Solutions
- Check the client's HTTP method matches the method filter bound in the route (warp::get(), warp::post(), etc.) and fix the client or add the missing method filter
- Compose the route with additional method filters if the endpoint should accept multiple methods (warp::get().or(warp::post()))
- Verify any reverse proxy/load balancer isn't rewriting the method
- Handle the rejection explicitly with .recover() to return a proper 405 with an Allow header
Example fix
// before
let routes = warp::post().and(warp::path("items")).map(create_item);
// client calls GET /items -> method_not_allowed
// after
let routes = warp::get()
.and(warp::path("items"))
.map(list_items)
.or(warp::post().and(warp::path("items")).map(create_item)); Defensive patterns
Strategy: try-catch
Validate before calling
// client-side: ensure the verb matches the endpoint's contract
const ALLOWED = ['GET','POST','PUT','DELETE'];
if (!ALLOWED.includes(request.method)) throw new Error(`use one of ${ALLOWED} for ${url}`); Type guard
fn is_supported_method(m: &Method) -> bool {
matches!(m, &Method::GET | &Method::POST | &Method::PUT | &Method::DELETE | &Method::HEAD | &Method::OPTIONS)
} Try / catch
let resp = routes.recover(|rej: warp::Rejection| async move {
if rej.is_not_found() || rej.find::<crate::reject::MethodNotAllowed>().is_some() {
Ok(warp::reply::with_status(warp::reply(), warp::http::StatusCode::METHOD_NOT_ALLOWED))
} else { Err(rej) }
}); Prevention
- Bind every intended HTTP method in the route with .or() composition
- Document each endpoint's allowed verbs and generate clients from them (OpenAPI)
- Test each endpoint with all verbs to catch 405s early
- Check proxy configs for method rewriting
When it happens
Trigger: A request whose path matches a filter chain but whose HTTP method was bound to a different method — e.g. calling warp::post() on a route the client hits with GET, or chaining warp::get() and warp::post() on separate routes where the client uses PATCH/PUT which was never registered.
Common situations: Clients using the wrong verb against a REST endpoint; forgetting to add warp::put() or warp::patch() for an endpoint; a frontend sending GET with a body where the server expects POST; CORS preflight OPTIONS requests not handled because warp::options() wasn't composed; proxy or client library defaulting to GET.
Understand the failure class
Background: "API error: {status}" and "HTTP 401/403/404/429/5xx" errors: non-2xx HTTP responses explained — this error's family across 27 libraries.
Related errors
AI-assisted analysis of seanmonstar/warp@ff34d7213e (2026-09-09).
Data as JSON: /api/errors/39391e06fe537db4.
Report an issue: GitHub.
Appendix: source
Thrown at src/filters/method.rs:138
/// ```
pub fn method() -> impl Filter<Extract = One<Method>, Error = Infallible> + Copy {
filter_fn_one(|route| future::ok::<_, Infallible>(route.method().clone()))
}
// NOTE: This takes a static function instead of `&'static Method` directly
// so that the `impl Filter` can be zero-sized. Moving it around should be
// cheaper than holding a single static pointer (which would make it 1 word).
fn method_is<F>(func: F) -> impl Filter<Extract = (), Error = Rejection> + Copy
where
F: Fn() -> &'static Method + Copy,
{
filter_fn(move |route| {
let method = func();
tracing::trace!("method::{:?}?: {:?}", method, route.method());
if route.method() == method {
future::ok(())
} else {
future::err(crate::reject::method_not_allowed())
}
})
}
#[cfg(test)]
mod tests {
#[test]
fn method_size_of() {
// See comment on `method_is` function.
assert_eq!(std::mem::size_of_val(&super::get()), 0,);
}
}
View on GitHub (pinned to ff34d7213e)