aio-libs/aiohttp · error · ValueError
boundary value contains invalid characters
Error message
boundary value contains invalid characters
What it means
The _boundary_value property formats the boundary for the Content-Type header. If the bytes are not a bare token (matching the tchar regex) AND they contain a character invalid even inside a quoted-string (matched by _invalid_qdtext_char_regex), ValueError is raised because the value cannot be serialized either as a token or as a quoted string.
Solutions
- Restrict boundaries to RFC 2046 bchars: A-Za-z0-9 and '()+,-./_:;=?\''.
- Let MultipartWriter autogenerate the boundary.
- Sanitize the candidate boundary: re.sub(r'[^A-Za-z0-9\'()+,-./_:;=?]', '', value).
- Unit-test boundary serialization by constructing the writer and reading its Content-Type header.
Example fix
// before mw = MultipartWriter(boundary='my boundary with space') // after mw = MultipartWriter(boundary='my_boundary_with_underscore')
Defensive patterns
Strategy: validation
Validate before calling
import re
_VALID_BOUNDARY = re.compile(r"[A-Za-z0-9'()+,./_:;=?-]+\Z")
def is_serializable_boundary(value: str) -> bool:
return bool(_VALID_BOUNDARY.match(value)) Type guard
import re
_TOKEN = re.compile(r"[A-Za-z0-9'()+,./_:;=?-]+\Z")
def is_token_boundary(value: object) -> bool:
return isinstance(value, str) and bool(_TOKEN.match(value)) Try / catch
try:
mw = MultipartWriter(boundary=candidate)
_ = mw.content_type # forces _boundary_value formatting
except ValueError as e:
if 'invalid characters' in str(e):
mw = MultipartWriter() # fall back to safe autogenerated boundary
else:
raise Prevention
- Restrict boundaries to RFC 2046 bchars to guarantee both token and quoted-string serializability.
- Avoid whitespace, control chars, backslashes, and quote characters in boundaries.
- Let MultipartWriter autogenerate the boundary.
When it happens
Trigger: A boundary containing control characters, NUL, bare backslash sequences that break quoted-string rules, or other bytes outside the union of tchar and valid qdtext/obs-text.
Common situations: User-supplied boundaries with whitespace, control chars, or delimiters; binary boundaries not filtered through the bchars set; boundaries copied from opaque tokens.
Understand the failure class
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- boundary missed for Content-Type
- boundary %r is too long (70 chars max)
- boundary should contain ASCII only chars
- Cannot create payload from %r
- content_type must be an instance of str. Got
AI-assisted analysis of aio-libs/aiohttp@d041d4d0fd (2026-08-11).
Data as JSON: /api/errors/4ebdcc16d0a59897.
Report an issue: GitHub.
Appendix: source
Thrown at aiohttp/multipart.py:992
# 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 d041d4d0fd)