From 5b19a03003b491b9957efa9444c07aec0340d864 Mon Sep 17 00:00:00 2001 From: Ragnor Comerford Date: Wed, 27 May 2026 12:04:51 +0200 Subject: [PATCH] mr-668: align GET /graphs 405 body code with HTTP status MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The single-mode `GET /graphs` handler returned an `ApiError` built via struct literal with `status: METHOD_NOT_ALLOWED, code: BadRequest`. The body code disagreed with the HTTP status — clients deserializing on `code` saw `bad_request`, clients deserializing on `status` saw 405. Same bug class as the earlier 503+Conflict mismatch on the removed YAML drift path. Close the class for this one remaining instance: * Add `ErrorCode::MethodNotAllowed` to the API enum. * Add `ApiError::method_not_allowed(msg)` — pairs the 405 status with the matching code. * Replace the struct literal in `server_graphs_list` with the constructor. * Regenerate `openapi.json` (adds `method_not_allowed` to the ErrorCode schema enum). Co-Authored-By: Claude Opus 4.7 (1M context) --- crates/omnigraph-server/src/api.rs | 5 +++++ crates/omnigraph-server/src/lib.rs | 24 +++++++++++++++++------- openapi.json | 1 + 3 files changed, 23 insertions(+), 7 deletions(-) diff --git a/crates/omnigraph-server/src/api.rs b/crates/omnigraph-server/src/api.rs index 24374fa..3af59ab 100644 --- a/crates/omnigraph-server/src/api.rs +++ b/crates/omnigraph-server/src/api.rs @@ -344,6 +344,11 @@ pub enum ErrorCode { Forbidden, BadRequest, NotFound, + /// 405 Method Not Allowed — the route exists but the active server + /// mode doesn't serve this method (e.g. `GET /graphs` in single-graph + /// mode). Distinct from 404 so clients can tell "wrong context" from + /// "no such resource." + MethodNotAllowed, Conflict, /// 429 Too Many Requests — per-actor admission cap exceeded. /// Clients should respect the `Retry-After` header. diff --git a/crates/omnigraph-server/src/lib.rs b/crates/omnigraph-server/src/lib.rs index ee19c01..dfa5799 100644 --- a/crates/omnigraph-server/src/lib.rs +++ b/crates/omnigraph-server/src/lib.rs @@ -567,6 +567,20 @@ impl ApiError { } } + /// HTTP 405 Method Not Allowed. Used when the route is mounted but + /// the active server mode doesn't serve it (`GET /graphs` in + /// single-graph mode returns this instead of 404 so clients can + /// distinguish "wrong context" from "no such resource"). + pub fn method_not_allowed(message: impl Into) -> Self { + Self { + status: StatusCode::METHOD_NOT_ALLOWED, + code: ErrorCode::MethodNotAllowed, + message: message.into(), + merge_conflicts: Vec::new(), + manifest_conflict: None, + } + } + pub fn conflict(message: impl Into) -> Self { Self { status: StatusCode::CONFLICT, @@ -1155,13 +1169,9 @@ async fn server_graphs_list( // 405 in single mode — there's no registry to enumerate, and the // legacy URL surface didn't expose this endpoint. if matches!(state.mode(), ServerMode::Single { .. }) { - return Err(ApiError { - status: StatusCode::METHOD_NOT_ALLOWED, - code: ErrorCode::BadRequest, - message: "GET /graphs is only available in multi-graph mode".to_string(), - merge_conflicts: Vec::new(), - manifest_conflict: None, - }); + return Err(ApiError::method_not_allowed( + "GET /graphs is only available in multi-graph mode", + )); } // Server-level Cedar gate. `state.server_policy` is loaded from diff --git a/openapi.json b/openapi.json index f5bc244..041531c 100644 --- a/openapi.json +++ b/openapi.json @@ -1256,6 +1256,7 @@ "forbidden", "bad_request", "not_found", + "method_not_allowed", "conflict", "too_many_requests", "internal"