{"id":"0ee2b986c89bea7a","repo":"aio-libs/aiohttp","slug":"bad-content-for-quoted-string-content-r","errorCode":null,"errorMessage":"bad content for quoted-string {content!r}","messagePattern":"bad content for quoted-string (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"aiohttp/helpers.py","lineNumber":412,"sourceCode":"    if name and isinstance(name, str) and name[0] != \"<\" and name[-1] != \">\":\n        return Path(name).name\n    return default\n\n\nnot_qtext_re = re.compile(r\"[^\\041\\043-\\133\\135-\\176]\")\nQCONTENT = {chr(i) for i in range(0x20, 0x7F)} | {\"\\t\"}\n\n\ndef quoted_string(content: str) -> str:\n    \"\"\"Return 7-bit content as quoted-string.\n\n    Format content into a quoted-string as defined in RFC5322 for\n    Internet Message Format. Notice that this is not the 8-bit HTTP\n    format, but the 7-bit email format. Content must be in usascii or\n    a ValueError is raised.\n    \"\"\"\n    if not (QCONTENT > set(content)):\n        raise ValueError(f\"bad content for quoted-string {content!r}\")\n    return not_qtext_re.sub(lambda x: \"\\\\\" + x.group(0), content)\n\n\ndef content_disposition_header(\n    disptype: str,\n    quote_fields: bool = True,\n    _charset: str = \"utf-8\",\n    params: dict[str, str] | None = None,\n) -> str:\n    \"\"\"Sets ``Content-Disposition`` header for MIME.\n\n    This is the MIME payload Content-Disposition header from RFC 2183\n    and RFC 7579 section 4.2, not the HTTP Content-Disposition from\n    RFC 6266.\n\n    disptype is a disposition type: inline, attachment, form-data.\n    Should be valid extension token (see RFC 2183)\n","sourceCodeStart":394,"sourceCodeEnd":430,"githubUrl":"https://github.com/aio-libs/aiohttp/blob/c0ef574e29109210e96e652771ae4e7b88615fa4/aiohttp/helpers.py#L394-L430","documentation":"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.","triggerScenarios":"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.","commonSituations":"User-supplied Unicode filenames passed to quoted_string directly; control chars in metadata; emoji in field names.","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."],"exampleFix":"// before\nquoted_string('résumé.pdf')  # raises\n// after\ncontent_disposition_header('attachment', quote_fields=True, params={'filename': 'résumé.pdf'})  # auto-fallback to filename*","handlingStrategy":"validation","validationCode":"QCONTENT = {chr(i) for i in range(0x20, 0x7F)} | {'\\t'}\ndef is_quoted_string_safe(s) -> bool:\n    return QCONTENT > set(s)","typeGuard":"def is_ascii_printable(s) -> bool:\n    return all('\\t' <= ch <= '~' for ch in s)","tryCatchPattern":null,"preventionTips":["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."],"tags":["content-disposition","mime","encoding","validation"],"analyzedSha":"c0ef574e29109210e96e652771ae4e7b88615fa4","analyzedAt":"2026-08-04T19:51:05.467Z","schemaVersion":2}