#1438·Robyn

Query parameters and path parameters are never percent-decoded

Author: saby1101Created Aug 31, 2026Updated Sep 13, 2026

What happens

No accessor on Request percent-decodes. Query values and path parameters are returned exactly as they appear on the wire, so %20 never becomes a space and %3A never becomes a colon.

Measured on Robyn 0.88.0, CPython 3.12, Linux:

python
from robyn import Robyn, Request

app = Robyn(__file__)

@app.get("/q")
def q(request: Request):
    p = request.query_params
    return str({"get": p.get("v", None), "to_dict": p.to_dict(), "items": dict(p.items())})

@app.get("/p/:seg")
def p(request: Request):
    return str(request.path_params["seg"])

app.start(host="127.0.0.1", port=8080)
GET /q?v=a%20b
  get("v", None) -> 'a%20b'
  to_dict()      -> {'v': ['a%20b']}
  items()        -> {'v': 'a%20b'}

GET /q?v=2026-01-01T00%3A00%3A00Z
  get("v", None) -> '2026-01-01T00%3A00%3A00Z'

GET /p/attempt%3A1
  path_params["seg"] -> 'attempt%3A1'

Expected

a%20b should arrive as a b, and attempt%3A1 as attempt:1. A space is the most common percent-encoded character in a query string, so this affects almost any client that encodes correctly.

Where it comes from

src/types/request.rs builds the query map by splitting the raw query string on & and = and storing the resulting substrings directly, with no decoding step. The path-parameter map is populated the same way.

For comparison, actix-web's own web::Query extractor decodes, so this is Robyn's layer rather than anything inherited.

Workaround

Call urllib.parse.unquote on every value at the edge of each handler.

Environment

Robyn 0.88.0 (current release at time of filing), CPython 3.12, Linux.