aio-libs/aiohttp · error · ValueError
bad content for quoted-string
Error message
bad content for quoted-string {content!r} What it means
quoted_string raises ValueError when the content string contains characters outside the QCONTENT set (printable 7-bit ASCII 0x20-0x7E plus tab). This function formats values as RFC 5322 quoted-strings for Content-Disposition headers, which require 7-bit US-ASCII content.
Solutions
- Let content_disposition_header handle it — it catches quoted_string ValueError and falls back to RFC 5987 extended notation (filename* with charset encoding)
- If calling quoted_string directly, sanitize content to 7-bit ASCII first
- Use the filename* parameter approach: encode non-ASCII filenames with urllib.parse.quote
Example fix
# before (direct call fails on non-ASCII)
from aiohttp.helpers import quoted_string
quoted_string('café.pdf') # ValueError
# after (let content_disposition_header handle encoding)
from aiohttp.helpers import content_disposition_header
header = content_disposition_header('attachment', params={'filename': 'café.pdf'})
# produces: attachment; filename*=utf-8''caf%C3%A9.pdf Defensive patterns
Strategy: validation
Validate before calling
QCONTENT = {chr(i) for i in range(0x20, 0x7F)} | {'\t'}
def is_quoted_string_safe(content: str) -> bool:
return bool(content) and QCONTENT > set(content)
if not is_quoted_string_safe(my_content):
# Encode for RFC 5987 extended notation instead
from urllib.parse import quote
content = f"utf-8''{quote(my_content, encoding='utf-8')}" Type guard
def is_ascii_printable(s: str) -> bool:
return all(0x20 <= ord(c) <= 0x7E or c == '\t' for c in s) Try / catch
from aiohttp.helpers import quoted_string
try:
result = quoted_string(content)
except ValueError:
from urllib.parse import quote
result = f"utf-8''{quote(content, encoding='utf-8')}" Prevention
- Prefer content_disposition_header() over calling quoted_string() directly — it handles fallback encoding
- Sanitize or URL-encode non-ASCII filenames before using them in headers
- Test multipart uploads with internationalized filenames
When it happens
Trigger: Calling helpers.quoted_string(content) or indirectly via content_disposition_header when a disposition parameter value contains non-ASCII characters (e.g. Unicode filenames with accented characters, CJK text, emoji) and quote_fields is True.
Common situations: Uploading a file with a Unicode name (e.g. 'café.pdf', 'データ.xlsx') as a multipart form field; Content-Disposition parameters with emoji or other non-ASCII content; content_disposition_header called with internationalized filenames.
Related errors
- bad content disposition parameter
- bad content disposition type
- boundary should contain ASCII only chars
- 1007
- 1007
AI-assisted analysis of aio-libs/aiohttp@d041d4d0fd (2026-08-11).
Data as JSON: /api/errors/0ee2b986c89bea7a.
Report an issue: GitHub.
Appendix: 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 d041d4d0fd)