aio-libs/aiohttp · error · ValueError
boundary value contains invalid characters
Error message
boundary value contains invalid characters
What it means
Raised by MultipartWriter._boundary_value when the boundary, not expressible as a bare HTTP token, must be quoted but contains characters that are invalid even inside a quoted-string (control chars or DEL, per the qdtext grammar). The boundary then cannot be serialized into a valid Content-Type header.
Source
Thrown at aiohttp/multipart.py:983
# Refer to RFCs 7231, 7230, 5234.
#
# parameter = token "=" ( token / quoted-string )
# token = 1*tchar
# quoted-string = DQUOTE *( qdtext / quoted-pair ) DQUOTE
# qdtext = HTAB / SP / %x21 / %x23-5B / %x5D-7E / obs-text
# obs-text = %x80-FF
# quoted-pair = "\" ( HTAB / SP / VCHAR / obs-text )
# tchar = "!" / "#" / "$" / "%" / "&" / "'" / "*"
# / "+" / "-" / "." / "^" / "_" / "`" / "|" / "~"
# / DIGIT / ALPHA
# ; any VCHAR, except delimiters
# VCHAR = %x21-7E
value = self._boundary
if re.match(self._valid_tchar_regex, value):
return value.decode("ascii") # cannot fail
if re.search(self._invalid_qdtext_char_regex, value):
raise ValueError("boundary value contains invalid characters")
# escape %x5C and %x22
quoted_value_content = value.replace(b"\\", b"\\\\")
quoted_value_content = quoted_value_content.replace(b'"', b'\\"')
return '"' + quoted_value_content.decode("ascii") + '"'
@property
def boundary(self) -> str:
return self._boundary.decode("ascii")
def append(self, obj: Any, headers: Mapping[str, str] | None = None) -> Payload:
if headers is None:
headers = CIMultiDict()
if isinstance(obj, Payload):
obj.headers.update(headers)
return self.append_payload(obj)View on GitHub (pinned to c0ef574e29)
Solutions
- Restrict the boundary to RFC 2046 token characters: alphanumeric plus `!#$%&'*+-.^_`|~`.
- Let MultipartWriter generate its own boundary (uuid4 hex) instead of passing a custom one.
- Sanitize any dynamic boundary with a whitelist regex before construction.
Example fix
// before
w = MultipartWriter(boundary='a\x00b')
// after
import re
safe = re.sub(rb'[^!#$%&\'*+\-.^_`|~\w]', b'', boundary)
w = MultipartWriter(boundary=safe.decode('ascii'))
Defensive patterns
Strategy: validation
Validate before calling
import re
token_re = re.compile(rb"\A[!#$%&'*+\-.^_`|~\w]+\Z")
if not token_re.match(boundary.encode('ascii')):
raise ValueError('boundary must be token-safe or quotable') Type guard
import re
_token = re.compile(rb"\A[!#$%&'*+\-.^_`|~\w]+\Z")
def is_valid_boundary(b: str) -> bool:
return bool(_token.match(b.encode('ascii', 'ignore'))) Try / catch
try:
writer = MultipartWriter(boundary=boundary)
except ValueError:
writer = MultipartWriter() # auto-generate a safe boundary Prevention
- Restrict boundaries to RFC token characters.
- Never derive a boundary from raw binary including control chars.
- Validate dynamic boundaries with a whitelist regex.
When it happens
Trigger: Constructing a MultipartWriter whose boundary contains bytes outside the token charset AND outside the allowed qdtext range (e.g. NUL, CR, LF, other control characters). The token regex fails, so the code attempts to quote it, then the qdtext validity check fails.
Common situations: Boundary derived from arbitrary binary/uuid bytes that include control characters; a typo introducing a non-printable character; fuzz-generated boundary values.
Related errors
- boundary %r is too long (70 chars max)
- boundary should contain ASCII only chars
- filename must be an instance of str. Got: %s
- content_type must be an instance of str. Got: %s
- invalid Content-Length: {length!r}
AI-assisted analysis of aio-libs/aiohttp@c0ef574e29 (2026-08-04).
Data as JSON: /data/errors/4ebdcc16d0a59897.json.
Report an issue: GitHub.