aio-libs/aiohttp · error · ValueError
bad content for quoted-string {content!r}
Error message
bad content for quoted-string {content!r} What it means
Raised by quoted_string() when the content contains characters outside QCONTENT (printable 7-bit US-ASCII 0x20-0x7E plus tab). The function formats MIME quoted-strings per RFC 5322, which is 7-bit only; any 8-bit/control char is rejected with ValueError before quoting.
Source
Thrown at aiohttp/helpers.py:412
if name and isinstance(name, str) and name[0] != "<" and name[-1] != ">":
return Path(name).name
return default
not_qtext_re = re.compile(r"[^\041\043-\133\135-\176]")
QCONTENT = {chr(i) for i in range(0x20, 0x7F)} | {"\t"}
def quoted_string(content: str) -> str:
"""Return 7-bit content as quoted-string.
Format content into a quoted-string as defined in RFC5322 for
Internet Message Format. Notice that this is not the 8-bit HTTP
format, but the 7-bit email format. Content must be in usascii or
a ValueError is raised.
"""
if not (QCONTENT > set(content)):
raise ValueError(f"bad content for quoted-string {content!r}")
return not_qtext_re.sub(lambda x: "\\" + x.group(0), content)
def content_disposition_header(
disptype: str,
quote_fields: bool = True,
_charset: str = "utf-8",
params: dict[str, str] | None = None,
) -> str:
"""Sets ``Content-Disposition`` header for MIME.
This is the MIME payload Content-Disposition header from RFC 2183
and RFC 7579 section 4.2, not the HTTP Content-Disposition from
RFC 6266.
disptype is a disposition type: inline, attachment, form-data.
Should be valid extension token (see RFC 2183)
View on GitHub (pinned to c0ef574e29)
Solutions
- Use content_disposition_header() (which gracefully falls back) instead of quoted_string() directly.
- Strip/normalize non-ASCII before calling quoted_string.
- Encode the value (e.g. percent-encoding) for 7-bit transport.
Example fix
// before
quoted_string('résumé.pdf') # raises
// after
content_disposition_header('attachment', quote_fields=True, params={'filename': 'résumé.pdf'}) # auto-fallback to filename* Defensive patterns
Strategy: validation
Validate before calling
QCONTENT = {chr(i) for i in range(0x20, 0x7F)} | {'\t'}
def is_quoted_string_safe(s) -> bool:
return QCONTENT > set(s) Type guard
def is_ascii_printable(s) -> bool:
return all('\t' <= ch <= '~' for ch in s) Prevention
- Prefer content_disposition_header() which auto-falls back to RFC 5987.
- Don't call quoted_string() on Unicode content directly.
- Percent-encode non-ASCII values for 7-bit transport.
When it happens
Trigger: content_disposition_header with quote_fields=True and a parameter value containing non-ASCII (Unicode filenames) — but the inner quoted_string() call is wrapped in try/except that falls back to RFC 5987 extended notation, so direct calls to quoted_string() are the real trigger. Calling helpers.quoted_string('café') directly.
Common situations: User-supplied Unicode filenames passed to quoted_string directly; control chars in metadata; emoji in field names.
Related errors
- bad content disposition type {disptype!r}
- bad content disposition parameter {key!r}={val!r}
- Compress wbits must between 9 and 15, zlib does not support
- 1007
- base_url must have a trailing '/'
AI-assisted analysis of aio-libs/aiohttp@c0ef574e29 (2026-08-04).
Data as JSON: /data/errors/0ee2b986c89bea7a.json.
Report an issue: GitHub.