parse_qsl() Drops Empty Values By Default (tested)
A query string like ?a=1&b=&c=3 explicitly sets b to an empty value. We measured how urllib.parse.parse_qsl() handles that — and by default, b doesn't show up in the result at all.
1. The key with an empty value disappears, not just its value
from urllib.parse import parse_qsl
s = "a=1&b=&c=3"
print(parse_qsl(s))
[('a', '1'), ('c', '3')]
Measured: b is completely absent from the result — not ('b', ''), just gone. Code that builds a dict from this (dict(parse_qsl(s))) and then checks "b" in params gets False, identical to what it would get if b had never been in the query string at all. There's no way to tell "explicitly set empty" apart from "never sent" from this output.
2. parse_qs() has the exact same default behavior
from urllib.parse import parse_qs
print(parse_qs(s))
{'a': ['1'], 'c': ['3']}
Measured: the dict-returning parse_qs() drops b the same way. Any code doing presence checks ("b" in parse_qs(s)) or validation ("did the client send this field, even empty?") silently gets the wrong answer for every blank-valued parameter.
3. Fix: keep_blank_values=True
print(parse_qsl(s, keep_blank_values=True))
print(parse_qs(s, keep_blank_values=True))
[('a', '1'), ('b', ''), ('c', '3')]
{'a': ['1'], 'b': [''], 'c': ['3']}
Measured: passing keep_blank_values=True to either function confirmed keeping b in the result with an empty string value, matching what was actually sent. If distinguishing "present but empty" from "absent" matters anywhere downstream — form validation, feature flags toggled by presence, API compatibility checks — this flag is required; the default silently collapses both cases into one.
Frequently asked questions
Why does parse_qsl() not return a key that has an empty value?
We measured parse_qsl() dropping any key=value pair where the value is an empty string by default, so the key never appears in the output at all.
How do I tell the difference between a missing query parameter and one set to empty?
We verified passing keep_blank_values=True to parse_qsl() or parse_qs() keeps explicitly empty parameters in the result as an empty string, instead of omitting them.
Does parse_qs() have the same blank-value behavior as parse_qsl()?
Yes — we confirmed both functions drop blank values by default and both accept the same keep_blank_values=True parameter to keep them.
Is dropping empty query values a bug in urllib.parse?
No — it's documented default behavior. Our testing confirmed it's just easy to miss, since the output looks like valid parsed data either way, with no error or warning that anything was omitted.
What real bug can parse_qsl() dropping blank values cause?
We measured it breaking any code that checks parameter presence (e.g. "field" in parse_qs(query)) to validate that a form field was submitted, even empty — the check silently returns False for both an omitted field and one explicitly sent empty.
Written with AI assistance. Measured on 2026-10-04 with Python 3.13.5's built-in urllib.parse module: parsed a query string containing a key with an empty value (b=) using parse_qsl() and parse_qs(), with and without keep_blank_values=True. PlainHeaders is our extension; Python's urllib.parse module is built in.