FA-76391 / Email MIME structure / Open access
Resolve a part's effective content type and charset: charset scope · case 01
Non-text parts report a charset taken from a meaningless parameter.
ROOT CAUSE
The charset scope decision evaluates `else: charset = charset` where the contract requires `else: charset = None`.
VERIFIED REPAIR
Use `else: charset = None` for the charset scope decision and keep every other rule of the model unchanged.
Unsuccessful approach: Clearing only the default charset still reports explicit charsets on binary parts. The attempted `else: charset = None if charset == 'us-ascii' else charset` still disagrees with a fixture.
Case contract
header is the raw Content-Type value or None. The type is the lower-cased text before ";" with surrounding spaces removed; it is valid only with a non-empty major and minor separated by "/" and no inner space. Missing or invalid types default to message/rfc822 inside multipart/digest and text/plain everywhere else (parameters of an invalid header are ignored). text/* types get the charset parameter (quotes removed, lower-cased) or us-ascii; other types have charset None. Result [type, charset].
Why this case matters
Default and fallback types decide whether a part is rendered as text, as a nested message or as an attachment.
1 / The failure
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(parent_type, header):
default = 'message/rfc822' if parent_type == 'multipart/digest' else 'text/plain'
mtype = default
charset = None
if header is not None:
head, _, rest = header.partition(';')
cand = head.strip().lower()
major, slash, minor = cand.partition('/')
if slash and major and minor and ' ' not in cand:
mtype = cand
for piece in rest.split(';'):
k, eq, v = piece.partition('=')
if eq and k.strip().lower() == 'charset':
charset = v.strip().strip('"').lower()
if mtype.startswith('text/'):
charset = charset or 'us-ascii'
else:
charset = charset
return [mtype, charset]
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
_CASES = {1: [('digest child without header', ('multipart/digest', None), ['message/rfc822', None]), ('mixed child without header', ('multipart/mixed', None), ['text/plain', 'us-ascii']), ('truncated type in digest', ('multipart/digest', 'text/'), ['message/rfc822', None]), ('missing major type', ('multipart/mixed', '/html; charset=utf-8'), ['text/plain', 'us-ascii']), ('quoted upper-case charset', ('multipart/mixed', 'Text/HTML; charset="UTF-8"'), ['text/html', 'utf-8']), ('charset on a binary type', ('multipart/mixed', 'image/png; charset=binary'), ['image/png', None]), ('padded header', ('multipart/alternative', ' text/plain ; charset=ISO-8859-1'), ['text/plain', 'iso-8859-1']), ('nested message parent', ('message/rfc822', None), ['text/plain', 'us-ascii'])], 2: [('digest child without header', ('multipart/digest', None), ['message/rfc822', None]), ('mixed child without header', ('multipart/mixed', None), ['text/plain', 'us-ascii']), ('truncated type in digest', ('multipart/digest', 'text/'), ['message/rfc822', None]), ('missing major type', ('multipart/mixed', '/html; charset=utf-8'), ['text/plain', 'us-ascii']), ('quoted upper-case charset', ('multipart/mixed', 'Text/HTML; charset="Windows-1252"'), ['text/html', 'windows-1252']), ('charset on a binary type', ('multipart/mixed', 'image/png; charset=binary'), ['image/png', None]), ('padded header', ('multipart/alternative', ' text/plain ; charset=ISO-8859-2'), ['text/plain', 'iso-8859-2']), ('nested message parent', ('message/rfc822', None), ['text/plain', 'us-ascii'])], 3: [('digest child without header', ('multipart/digest', None), ['message/rfc822', None]), ('mixed child without header', ('multipart/mixed', None), ['text/plain', 'us-ascii']), ('truncated type in digest', ('multipart/digest', 'text/'), ['message/rfc822', None]), ('missing major type', ('multipart/mixed', '/html; charset=utf-8'), ['text/plain', 'us-ascii']), ('quoted upper-case charset', ('multipart/mixed', 'Text/HTML; charset="KOI8-R"'), ['text/html', 'koi8-r']), ('charset on a binary type', ('multipart/mixed', 'image/png; charset=binary'), ['image/png', None]), ('padded header', ('multipart/alternative', ' text/plain ; charset=ISO-8859-3'), ['text/plain', 'iso-8859-3']), ('nested message parent', ('message/rfc822', None), ['text/plain', 'us-ascii'])], 4: [('digest child without header', ('multipart/digest', None), ['message/rfc822', None]), ('mixed child without header', ('multipart/mixed', None), ['text/plain', 'us-ascii']), ('truncated type in digest', ('multipart/digest', 'text/'), ['message/rfc822', None]), ('missing major type', ('multipart/mixed', '/html; charset=utf-8'), ['text/plain', 'us-ascii']), ('quoted upper-case charset', ('multipart/mixed', 'Text/HTML; charset="Shift_JIS"'), ['text/html', 'shift_jis']), ('charset on a binary type', ('multipart/mixed', 'image/png; charset=binary'), ['image/png', None]), ('padded header', ('multipart/alternative', ' text/plain ; charset=ISO-8859-4'), ['text/plain', 'iso-8859-4']), ('nested message parent', ('message/rfc822', None), ['text/plain', 'us-ascii'])], 5: [('digest child without header', ('multipart/digest', None), ['message/rfc822', None]), ('mixed child without header', ('multipart/mixed', None), ['text/plain', 'us-ascii']), ('truncated type in digest', ('multipart/digest', 'text/'), ['message/rfc822', None]), ('missing major type', ('multipart/mixed', '/html; charset=utf-8'), ['text/plain', 'us-ascii']), ('quoted upper-case charset', ('multipart/mixed', 'Text/HTML; charset="EUC-KR"'), ['text/html', 'euc-kr']), ('charset on a binary type', ('multipart/mixed', 'image/png; charset=binary'), ['image/png', None]), ('padded header', ('multipart/alternative', ' text/plain ; charset=ISO-8859-5'), ['text/plain', 'iso-8859-5']), ('nested message parent', ('message/rfc822', None), ['text/plain', 'us-ascii'])]}
for _label, _args, _expected in _CASES[N]:
check(_label, solve(*_args), _expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
| Boundary fixture | Actual | Expected | Outcome |
|---|---|---|---|
| digest child without header | ['message/rfc822', None] | ['message/rfc822', None] | Passed |
| mixed child without header | ['text/plain', 'us-ascii'] | ['text/plain', 'us-ascii'] | Passed |
| truncated type in digest | ['message/rfc822', None] | ['message/rfc822', None] | Passed |
| missing major type | ['text/plain', 'us-ascii'] | ['text/plain', 'us-ascii'] | Passed |
| quoted upper-case charset | ['text/html', 'utf-8'] | ['text/html', 'utf-8'] | Passed |
| charset on a binary type | ['image/png', 'binary'] | ['image/png', None] | Failed |
| padded header | ['text/plain', 'iso-8859-1'] | ['text/plain', 'iso-8859-1'] | Passed |
| nested message parent | ['text/plain', 'us-ascii'] | ['text/plain', 'us-ascii'] | Passed |
SHA-256 / 57d9b7cdc41d30354bec38e1fcadd7abe0662f1983bfffa21a56194951e8dd0d
2 / The unsuccessful fix
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(parent_type, header):
default = 'message/rfc822' if parent_type == 'multipart/digest' else 'text/plain'
mtype = default
charset = None
if header is not None:
head, _, rest = header.partition(';')
cand = head.strip().lower()
major, slash, minor = cand.partition('/')
if slash and major and minor and ' ' not in cand:
mtype = cand
for piece in rest.split(';'):
k, eq, v = piece.partition('=')
if eq and k.strip().lower() == 'charset':
charset = v.strip().strip('"').lower()
if mtype.startswith('text/'):
charset = charset or 'us-ascii'
else:
charset = None if charset == 'us-ascii' else charset
return [mtype, charset]
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
_CASES = {1: [('digest child without header', ('multipart/digest', None), ['message/rfc822', None]), ('mixed child without header', ('multipart/mixed', None), ['text/plain', 'us-ascii']), ('truncated type in digest', ('multipart/digest', 'text/'), ['message/rfc822', None]), ('missing major type', ('multipart/mixed', '/html; charset=utf-8'), ['text/plain', 'us-ascii']), ('quoted upper-case charset', ('multipart/mixed', 'Text/HTML; charset="UTF-8"'), ['text/html', 'utf-8']), ('charset on a binary type', ('multipart/mixed', 'image/png; charset=binary'), ['image/png', None]), ('padded header', ('multipart/alternative', ' text/plain ; charset=ISO-8859-1'), ['text/plain', 'iso-8859-1']), ('nested message parent', ('message/rfc822', None), ['text/plain', 'us-ascii'])], 2: [('digest child without header', ('multipart/digest', None), ['message/rfc822', None]), ('mixed child without header', ('multipart/mixed', None), ['text/plain', 'us-ascii']), ('truncated type in digest', ('multipart/digest', 'text/'), ['message/rfc822', None]), ('missing major type', ('multipart/mixed', '/html; charset=utf-8'), ['text/plain', 'us-ascii']), ('quoted upper-case charset', ('multipart/mixed', 'Text/HTML; charset="Windows-1252"'), ['text/html', 'windows-1252']), ('charset on a binary type', ('multipart/mixed', 'image/png; charset=binary'), ['image/png', None]), ('padded header', ('multipart/alternative', ' text/plain ; charset=ISO-8859-2'), ['text/plain', 'iso-8859-2']), ('nested message parent', ('message/rfc822', None), ['text/plain', 'us-ascii'])], 3: [('digest child without header', ('multipart/digest', None), ['message/rfc822', None]), ('mixed child without header', ('multipart/mixed', None), ['text/plain', 'us-ascii']), ('truncated type in digest', ('multipart/digest', 'text/'), ['message/rfc822', None]), ('missing major type', ('multipart/mixed', '/html; charset=utf-8'), ['text/plain', 'us-ascii']), ('quoted upper-case charset', ('multipart/mixed', 'Text/HTML; charset="KOI8-R"'), ['text/html', 'koi8-r']), ('charset on a binary type', ('multipart/mixed', 'image/png; charset=binary'), ['image/png', None]), ('padded header', ('multipart/alternative', ' text/plain ; charset=ISO-8859-3'), ['text/plain', 'iso-8859-3']), ('nested message parent', ('message/rfc822', None), ['text/plain', 'us-ascii'])], 4: [('digest child without header', ('multipart/digest', None), ['message/rfc822', None]), ('mixed child without header', ('multipart/mixed', None), ['text/plain', 'us-ascii']), ('truncated type in digest', ('multipart/digest', 'text/'), ['message/rfc822', None]), ('missing major type', ('multipart/mixed', '/html; charset=utf-8'), ['text/plain', 'us-ascii']), ('quoted upper-case charset', ('multipart/mixed', 'Text/HTML; charset="Shift_JIS"'), ['text/html', 'shift_jis']), ('charset on a binary type', ('multipart/mixed', 'image/png; charset=binary'), ['image/png', None]), ('padded header', ('multipart/alternative', ' text/plain ; charset=ISO-8859-4'), ['text/plain', 'iso-8859-4']), ('nested message parent', ('message/rfc822', None), ['text/plain', 'us-ascii'])], 5: [('digest child without header', ('multipart/digest', None), ['message/rfc822', None]), ('mixed child without header', ('multipart/mixed', None), ['text/plain', 'us-ascii']), ('truncated type in digest', ('multipart/digest', 'text/'), ['message/rfc822', None]), ('missing major type', ('multipart/mixed', '/html; charset=utf-8'), ['text/plain', 'us-ascii']), ('quoted upper-case charset', ('multipart/mixed', 'Text/HTML; charset="EUC-KR"'), ['text/html', 'euc-kr']), ('charset on a binary type', ('multipart/mixed', 'image/png; charset=binary'), ['image/png', None]), ('padded header', ('multipart/alternative', ' text/plain ; charset=ISO-8859-5'), ['text/plain', 'iso-8859-5']), ('nested message parent', ('message/rfc822', None), ['text/plain', 'us-ascii'])]}
for _label, _args, _expected in _CASES[N]:
check(_label, solve(*_args), _expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
| Boundary fixture | Actual | Expected | Outcome |
|---|---|---|---|
| digest child without header | ['message/rfc822', None] | ['message/rfc822', None] | Passed |
| mixed child without header | ['text/plain', 'us-ascii'] | ['text/plain', 'us-ascii'] | Passed |
| truncated type in digest | ['message/rfc822', None] | ['message/rfc822', None] | Passed |
| missing major type | ['text/plain', 'us-ascii'] | ['text/plain', 'us-ascii'] | Passed |
| quoted upper-case charset | ['text/html', 'utf-8'] | ['text/html', 'utf-8'] | Passed |
| charset on a binary type | ['image/png', 'binary'] | ['image/png', None] | Failed |
| padded header | ['text/plain', 'iso-8859-1'] | ['text/plain', 'iso-8859-1'] | Passed |
| nested message parent | ['text/plain', 'us-ascii'] | ['text/plain', 'us-ascii'] | Passed |
SHA-256 / 08493d4677e5920a13826819310f537cf9c022e0c2df5e6a8b0dc9e2b31c7d6d
3 / The verified repair
Exit 0"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(parent_type, header):
default = 'message/rfc822' if parent_type == 'multipart/digest' else 'text/plain'
mtype = default
charset = None
if header is not None:
head, _, rest = header.partition(';')
cand = head.strip().lower()
major, slash, minor = cand.partition('/')
if slash and major and minor and ' ' not in cand:
mtype = cand
for piece in rest.split(';'):
k, eq, v = piece.partition('=')
if eq and k.strip().lower() == 'charset':
charset = v.strip().strip('"').lower()
if mtype.startswith('text/'):
charset = charset or 'us-ascii'
else:
charset = None
return [mtype, charset]
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
_CASES = {1: [('digest child without header', ('multipart/digest', None), ['message/rfc822', None]), ('mixed child without header', ('multipart/mixed', None), ['text/plain', 'us-ascii']), ('truncated type in digest', ('multipart/digest', 'text/'), ['message/rfc822', None]), ('missing major type', ('multipart/mixed', '/html; charset=utf-8'), ['text/plain', 'us-ascii']), ('quoted upper-case charset', ('multipart/mixed', 'Text/HTML; charset="UTF-8"'), ['text/html', 'utf-8']), ('charset on a binary type', ('multipart/mixed', 'image/png; charset=binary'), ['image/png', None]), ('padded header', ('multipart/alternative', ' text/plain ; charset=ISO-8859-1'), ['text/plain', 'iso-8859-1']), ('nested message parent', ('message/rfc822', None), ['text/plain', 'us-ascii'])], 2: [('digest child without header', ('multipart/digest', None), ['message/rfc822', None]), ('mixed child without header', ('multipart/mixed', None), ['text/plain', 'us-ascii']), ('truncated type in digest', ('multipart/digest', 'text/'), ['message/rfc822', None]), ('missing major type', ('multipart/mixed', '/html; charset=utf-8'), ['text/plain', 'us-ascii']), ('quoted upper-case charset', ('multipart/mixed', 'Text/HTML; charset="Windows-1252"'), ['text/html', 'windows-1252']), ('charset on a binary type', ('multipart/mixed', 'image/png; charset=binary'), ['image/png', None]), ('padded header', ('multipart/alternative', ' text/plain ; charset=ISO-8859-2'), ['text/plain', 'iso-8859-2']), ('nested message parent', ('message/rfc822', None), ['text/plain', 'us-ascii'])], 3: [('digest child without header', ('multipart/digest', None), ['message/rfc822', None]), ('mixed child without header', ('multipart/mixed', None), ['text/plain', 'us-ascii']), ('truncated type in digest', ('multipart/digest', 'text/'), ['message/rfc822', None]), ('missing major type', ('multipart/mixed', '/html; charset=utf-8'), ['text/plain', 'us-ascii']), ('quoted upper-case charset', ('multipart/mixed', 'Text/HTML; charset="KOI8-R"'), ['text/html', 'koi8-r']), ('charset on a binary type', ('multipart/mixed', 'image/png; charset=binary'), ['image/png', None]), ('padded header', ('multipart/alternative', ' text/plain ; charset=ISO-8859-3'), ['text/plain', 'iso-8859-3']), ('nested message parent', ('message/rfc822', None), ['text/plain', 'us-ascii'])], 4: [('digest child without header', ('multipart/digest', None), ['message/rfc822', None]), ('mixed child without header', ('multipart/mixed', None), ['text/plain', 'us-ascii']), ('truncated type in digest', ('multipart/digest', 'text/'), ['message/rfc822', None]), ('missing major type', ('multipart/mixed', '/html; charset=utf-8'), ['text/plain', 'us-ascii']), ('quoted upper-case charset', ('multipart/mixed', 'Text/HTML; charset="Shift_JIS"'), ['text/html', 'shift_jis']), ('charset on a binary type', ('multipart/mixed', 'image/png; charset=binary'), ['image/png', None]), ('padded header', ('multipart/alternative', ' text/plain ; charset=ISO-8859-4'), ['text/plain', 'iso-8859-4']), ('nested message parent', ('message/rfc822', None), ['text/plain', 'us-ascii'])], 5: [('digest child without header', ('multipart/digest', None), ['message/rfc822', None]), ('mixed child without header', ('multipart/mixed', None), ['text/plain', 'us-ascii']), ('truncated type in digest', ('multipart/digest', 'text/'), ['message/rfc822', None]), ('missing major type', ('multipart/mixed', '/html; charset=utf-8'), ['text/plain', 'us-ascii']), ('quoted upper-case charset', ('multipart/mixed', 'Text/HTML; charset="EUC-KR"'), ['text/html', 'euc-kr']), ('charset on a binary type', ('multipart/mixed', 'image/png; charset=binary'), ['image/png', None]), ('padded header', ('multipart/alternative', ' text/plain ; charset=ISO-8859-5'), ['text/plain', 'iso-8859-5']), ('nested message parent', ('message/rfc822', None), ['text/plain', 'us-ascii'])]}
for _label, _args, _expected in _CASES[N]:
check(_label, solve(*_args), _expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
| Boundary fixture | Actual | Expected | Outcome |
|---|---|---|---|
| digest child without header | ['message/rfc822', None] | ['message/rfc822', None] | Passed |
| mixed child without header | ['text/plain', 'us-ascii'] | ['text/plain', 'us-ascii'] | Passed |
| truncated type in digest | ['message/rfc822', None] | ['message/rfc822', None] | Passed |
| missing major type | ['text/plain', 'us-ascii'] | ['text/plain', 'us-ascii'] | Passed |
| quoted upper-case charset | ['text/html', 'utf-8'] | ['text/html', 'utf-8'] | Passed |
| charset on a binary type | ['image/png', None] | ['image/png', None] | Passed |
| padded header | ['text/plain', 'iso-8859-1'] | ['text/plain', 'iso-8859-1'] | Passed |
| nested message parent | ['text/plain', 'us-ascii'] | ['text/plain', 'us-ascii'] | Passed |
SHA-256 / 937ad6c7c2b1dfe1d377beba22461086b4ba8ea288085303e37e9d34693b027e
Verification & scope
Stipulated offline model over pre-parsed MIME dictionaries; not a conforming MIME parser, generator or mail client. This reproducer isolates one failure mechanism. Results cover the supplied fixtures. Variants within a family share a test contract and should remain grouped when constructing evaluation splits. Related mechanisms with a shared evaluation_group must also remain together; these controlled models are not independent production incidents.
Observations recorded using Python 3.12.14 at 2026-09-29T14:49:15.918878+00:00.
Case digest / d75d88e720b7198a74f4bab16b39f3b0472a5ad4480c92843f229bcdc9a6a1a5