aio-libs/aiohttp · error · ValueError
boundary should contain ASCII only chars
Error message
boundary should contain ASCII only chars
What it means
MultipartWriter.__init__ requires the boundary string to be ASCII-encodable, because the underlying Payload API serializes the boundary as a str and writes it into the Content-Type header. If boundary.encode('ascii') raises UnicodeEncodeError, ValueError is raised.
Solutions
- Pass an ASCII-only boundary (RFC 2046 bchars): digits, letters, and '()+,-./_:;=?\''.
- Omit the boundary argument and let MultipartWriter generate one via uuid.uuid4().hex.
- Sanitize user input before using it as a boundary: re.sub(r'[^A-Za-z0-9\'()+,-./_:;=?]', '', value).
- Validate with value.isascii() before constructing the writer.
Example fix
// before mw = MultipartWriter(boundary='CAFE-菜单') // after mw = MultipartWriter() # auto-generated ASCII boundary
Defensive patterns
Strategy: validation
Validate before calling
import re
_BCHARS = re.compile(r"[A-Za-z0-9'()+,./_:;=?-]+\Z")
def sanitize_boundary(value: str) -> str:
cleaned = re.sub(r"[^A-Za-z0-9'()+,./_:;=?-]", '', value)
return cleaned or uuid.uuid4().hex Type guard
def is_ascii_boundary(value: object) -> bool:
return isinstance(value, str) and value.isascii() Try / catch
try:
mw = MultipartWriter(boundary=candidate)
except ValueError as e:
if 'ASCII' in str(e):
mw = MultipartWriter() # fall back to autogenerated ASCII boundary
else:
raise Prevention
- Omit the boundary argument and let MultipartWriter autogenerate one.
- Never derive boundaries from user input or locale-specific strings.
- Restrict boundaries to RFC 2046 bchars.
When it happens
Trigger: Passing a boundary containing non-ASCII characters (e.g. emoji, accented letters, CJK) to MultipartWriter(subtype=..., boundary=...).
Common situations: Deriving the boundary from user-supplied data, file names, or locale-specific strings; copy-pasting a boundary from a non-ASCII source.
Related errors
- boundary value contains invalid characters
- boundary missed for Content-Type
- boundary %r is too long (70 chars max)
- bad content for quoted-string
- Cannot create payload from %r
AI-assisted analysis of aio-libs/aiohttp@d041d4d0fd (2026-08-11).
Data as JSON: /api/errors/74e55085e55a6e54.
Report an issue: GitHub.
Appendix: source
Thrown at aiohttp/multipart.py:928
class MultipartWriter(Payload):
"""Multipart body writer."""
_value: None
# _consumed = False (inherited) - Can be encoded multiple times
_autoclose = True # No file handles, just collects parts in memory
def __init__(self, subtype: str = "mixed", boundary: str | None = None) -> None:
boundary = boundary if boundary is not None else uuid.uuid4().hex
# The underlying Payload API demands a str (utf-8), not bytes,
# so we need to ensure we don't lose anything during conversion.
# As a result, require the boundary to be ASCII only.
# In both situations.
try:
self._boundary = boundary.encode("ascii")
except UnicodeEncodeError:
raise ValueError("boundary should contain ASCII only chars") from None
if len(boundary) > 70:
raise ValueError("boundary %r is too long (70 chars max)" % boundary)
ctype = f"multipart/{subtype}; boundary={self._boundary_value}"
super().__init__(None, content_type=ctype)
self._parts: list[_Part] = []
self._is_form_data = subtype == "form-data"
def __enter__(self) -> "MultipartWriter":
return self
def __exit__(
self,
exc_type: type[BaseException] | None,
exc_val: BaseException | None,View on GitHub (pinned to d041d4d0fd)