From 22d1a991aee19737cb87540f58bb8616ce78a03d Mon Sep 17 00:00:00 2001 From: Alok Subbarao <96636525+asubbarao@users.noreply.github.com> Date: Thu, 15 Oct 2026 10:28:27 -0700 Subject: [PATCH 1/2] server: invalid UTF-8 in bound params -> 422, not 500 DuckDB's Value(string) throws Invalid Input on invalid UTF-8. Three unprotected construction sites in HandleRequest's binding loop (BindParamValue, the number-like probe, the PARAM constraint check) let that throw escape as a 500 with a serialized-exception body -- and on POST /mcp it broke the JSON-RPC envelope with a raw HTTP 500. Probe once per parameter right after `raw` is assigned so malformed bytes become a 422 on the parameter that carried them, covering query/form/header/cookie/body locations. --- src/quackapi_server.cpp | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/quackapi_server.cpp b/src/quackapi_server.cpp index 386c26c..9ae44ba 100644 --- a/src/quackapi_server.cpp +++ b/src/quackapi_server.cpp @@ -4114,6 +4114,21 @@ void QuackapiHttpServer::HandleRequest(const duckdb_httplib::Request &req, duckd raw = it->second.second; } + // Client bytes are not guaranteed valid UTF-8 (query/form/header/ + // cookie decoders pass bytes through). DuckDB's Value(string) + // throws Invalid Input on invalid UTF-8, which would otherwise + // escape as a 500 with a serialized-exception body. Probe once + // here so malformed bytes become a 422 on the parameter that + // carried them. + try { + Value utf8_probe(raw); + (void)utf8_probe; + } catch (std::exception &) { + validation_issues.push_back({ValidationLocJson(loc_kind, param_name), + "Input should be valid UTF-8 text", "type_error"}); + continue; + } + // Strict integral check even when type is UNKNOWN: if the raw value // is number-like but not a strict integer, and a cast-to-int would // succeed via DuckDB rounding (1.5→2, 1e2→100), reject now. From 70fa8ee4b53d69adee0b06dfa918da5cec7f294c Mon Sep 17 00:00:00 2001 From: Alok Subbarao <96636525+asubbarao@users.noreply.github.com> Date: Fri, 16 Oct 2026 10:29:22 -0700 Subject: [PATCH 2/2] format: clang-format line break in utf8 validation push_back --- src/quackapi_server.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/quackapi_server.cpp b/src/quackapi_server.cpp index 9ae44ba..2d1103b 100644 --- a/src/quackapi_server.cpp +++ b/src/quackapi_server.cpp @@ -4124,8 +4124,8 @@ void QuackapiHttpServer::HandleRequest(const duckdb_httplib::Request &req, duckd Value utf8_probe(raw); (void)utf8_probe; } catch (std::exception &) { - validation_issues.push_back({ValidationLocJson(loc_kind, param_name), - "Input should be valid UTF-8 text", "type_error"}); + validation_issues.push_back( + {ValidationLocJson(loc_kind, param_name), "Input should be valid UTF-8 text", "type_error"}); continue; }