FAILURE MAP
← Case archive

FA-27796 / HTTP ranges / Open access

A segment playlist resolves byte-range tags into per-segment Range values: carriage returns from CRLF playlists are trimmed before parsing · case 01

A segment playlist resolves byte-range tags into per-segment Range values: carriage returns from CRLF playlists are trimmed before parsing.

Verified by executionVariant 1 · 15 checks per implementationDownload source bundle ↓JSON ↗

ROOT CAUSE

The playlist-byterange-crlf-lines decision uses out=[] pending=None prev=None for raw in x: line=raw if not line: continue if line.startswith('#EXT-X-BYTERANGE:'): n,at,o=line[17:].partition('@') if not n.isdigit() or (at and not o.isdigit()): return None pending=[int(n),int(o) if at else None] elif line.startswith('#'): continue else: if pending is None: out.append([line,None]) prev=None else: n,o=pending if o is None: if prev is None or prev[0]!=line: return None o=prev[1] if n==0: return None out.append([line,'bytes=%d-%d'%(o,o+n-1)]) prev=[line,o+n] pending=None return out.

VERIFIED REPAIR

Apply the bounded decision exactly: out=[] pending=None prev=None for raw in x: line=raw.strip() if not line: continue if line.startswith('#EXT-X-BYTERANGE:'): n,at,o=line[17:].partition('@') if not n.isdigit() or (at and not o.isdigit()): return None pending=[int(n),int(o) if at else None] elif line.startswith('#'): continue else: if pending is None: out.append([line,None]) prev=None else: n,o=pending if o is None: if prev is None or prev[0]!=line: return None o=prev[1] if n==0: return None out.append([line,'bytes=%d-%d'%(o,o+n-1)]) prev=[line,o+n] pending=None return out

Unsuccessful approach: The partial repair uses out=[] pending=None prev=None for raw in x: line=raw.rstrip('\n') if not line: continue if line.startswith('#EXT-X-BYTERANGE:'): n,at,o=line[17:].partition('@') if not n.isdigit() or (at and not o.isdigit()): return None pending=[int(n),int(o) if at else None] elif line.startswith('#'): continue else: if pending is None: out.append([line,None]) prev=None else: n,o=pending if o is None: if prev is None or prev[0]!=line: return None o=prev[1] if n==0: return None out.append([line,'bytes=%d-%d'%(o,o+n-1)]) prev=[line,o+n] pending=None return out, which still violates the stated contract.

Case contract

x is a list of playlist lines. Lines are trimmed; blank lines skipped; every other line starting with # is a tag or comment. #EXT-X-BYTERANGE:n[@o] (ASCII decimals) applies only to the next URI line. Without @o the range continues at the byte after the immediately previous segment, which must be a sub-range of the same URI string; otherwise null. A URI without a byte-range tag is a whole-resource segment [uri,null] and breaks any continuation chain. Zero-length sub-ranges and malformed tags return null. Return [uri,inclusive bytes=a-b or null] rows.

Why this case matters

Range responses combine representation identity, conditional requests, framing, and partial-object state.

1 / The failure

Exit 1
"""Failure Map reference implementation. Python standard library only."""
import json

N = 1
observations = []
def solve(x):
    out=[]
    pending=None
    prev=None
    for raw in x:
        line=raw
        if not line: continue
        if line.startswith('#EXT-X-BYTERANGE:'):
            n,at,o=line[17:].partition('@')
            if not n.isdigit() or (at and not o.isdigit()): return None
            pending=[int(n),int(o) if at else None]
        elif line.startswith('#'):
            continue
        else:
            if pending is None:
                out.append([line,None])
                prev=None
            else:
                n,o=pending
                if o is None:
                    if prev is None or prev[0]!=line: return None
                    o=prev[1]
                if n==0: return None
                out.append([line,'bytes=%d-%d'%(o,o+n-1)])
                prev=[line,o+n]
            pending=None
    return out
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
check('crlf-lines fixture 0', json.loads(json.dumps(solve(['#EXTM3U','#EXTINF:1,','#EXT-X-BYTERANGE:100@0','a.ts','#EXTINF:1,','#EXT-X-BYTERANGE:50','a.ts','#EXTINF:1,','#EXT-X-BYTERANGE:25','a.ts']))), json.loads(json.dumps([['a.ts','bytes=0-99'],['a.ts','bytes=100-149'],['a.ts','bytes=150-174']])))
check('crlf-lines fixture 1', json.loads(json.dumps(solve(['#EXTM3U','# generated by tool','#EXT-X-BYTERANGE:10@5','b.ts']))), json.loads(json.dumps([['b.ts','bytes=5-14']])))
check('crlf-lines fixture 2', json.loads(json.dumps(solve(['#EXT-X-BYTERANGE:10@0','a.ts','#EXT-X-BYTERANGE:10','b.ts']))), json.loads(json.dumps(None)))
check('crlf-lines fixture 3', json.loads(json.dumps(solve(['#EXT-X-BYTERANGE:10@0','a.ts?v=1','#EXT-X-BYTERANGE:10','a.ts?v=2']))), json.loads(json.dumps(None)))
check('crlf-lines fixture 4', json.loads(json.dumps(solve(['#EXT-X-BYTERANGE:10','a.ts']))), json.loads(json.dumps(None)))
check('crlf-lines fixture 5', json.loads(json.dumps(solve(['full.ts','#EXT-X-BYTERANGE:10','a.ts']))), json.loads(json.dumps(None)))
check('crlf-lines fixture 6', json.loads(json.dumps(solve(['#EXT-X-BYTERANGE:10@0','a.ts','a.ts','#EXT-X-BYTERANGE:10','a.ts']))), json.loads(json.dumps(None)))
check('crlf-lines fixture 7', json.loads(json.dumps(solve(['#EXT-X-BYTERANGE:10@0','a.ts','b.ts']))), json.loads(json.dumps([['a.ts','bytes=0-9'],['b.ts',None]])))
check('crlf-lines fixture 8', json.loads(json.dumps(solve(['#EXT-X-BYTERANGE:0@0','a.ts']))), json.loads(json.dumps(None)))
check('crlf-lines fixture 9', json.loads(json.dumps(solve(['#EXT-X-BYTERANGE:5@0','a.ts','#EXT-X-BYTERANGE:0','a.ts']))), json.loads(json.dumps(None)))
check('crlf-lines fixture 10', json.loads(json.dumps(solve(['#EXTM3U\r\n','#EXT-X-BYTERANGE:10@0\r\n','a.ts\r\n','#EXT-X-BYTERANGE:4\r\n','a.ts\r\n']))), json.loads(json.dumps([['a.ts','bytes=0-9'],['a.ts','bytes=10-13']])))
check('crlf-lines fixture 11', json.loads(json.dumps(solve(['#EXT-X-BYTERANGE:1x@0','a.ts']))), json.loads(json.dumps(None)))
check('crlf-lines fixture 12', json.loads(json.dumps(solve(['#EXT-X-BYTERANGE:10@','a.ts']))), json.loads(json.dumps(None)))
check('crlf-lines fixture 13', json.loads(json.dumps(solve(['','#EXT-X-BYTERANGE:3@7','   ','c.ts']))), json.loads(json.dumps([['c.ts','bytes=7-9']])))
check('crlf-lines fixture 14', json.loads(json.dumps(solve(["#EXT-X-BYTERANGE:%d@0"%N,'a.ts',"#EXT-X-BYTERANGE:%d"%N,'a.ts']))), json.loads(json.dumps([['a.ts',"bytes=0-%d"%(N-1)],['a.ts',"bytes=%d-%d"%(N,2*N-1)]])))
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 fixtureActualExpectedOutcome
crlf-lines fixture 0[['a.ts', 'bytes=0-99'], ['a.ts', 'bytes=100-149'], ['a.ts', 'bytes=150-174']][['a.ts', 'bytes=0-99'], ['a.ts', 'bytes=100-149'], ['a.ts', 'bytes=150-174']]Passed
crlf-lines fixture 1[['b.ts', 'bytes=5-14']][['b.ts', 'bytes=5-14']]Passed
crlf-lines fixture 2NoneNonePassed
crlf-lines fixture 3NoneNonePassed
crlf-lines fixture 4NoneNonePassed
crlf-lines fixture 5NoneNonePassed
crlf-lines fixture 6NoneNonePassed
crlf-lines fixture 7[['a.ts', 'bytes=0-9'], ['b.ts', None]][['a.ts', 'bytes=0-9'], ['b.ts', None]]Passed
crlf-lines fixture 8NoneNonePassed
crlf-lines fixture 9NoneNonePassed
crlf-lines fixture 10None[['a.ts', 'bytes=0-9'], ['a.ts', 'bytes=10-13']]Failed
crlf-lines fixture 11NoneNonePassed
crlf-lines fixture 12NoneNonePassed
crlf-lines fixture 13[[' ', 'bytes=7-9'], ['c.ts', None]][['c.ts', 'bytes=7-9']]Failed
crlf-lines fixture 14[['a.ts', 'bytes=0-0'], ['a.ts', 'bytes=1-1']][['a.ts', 'bytes=0-0'], ['a.ts', 'bytes=1-1']]Passed

SHA-256 / 249d245a48d6e60d61b8107acf4087bb725920643ca536846d9adacdcabbb86d

2 / The unsuccessful fix

Exit 1
"""Failure Map reference implementation. Python standard library only."""
import json

N = 1
observations = []
def solve(x):
    out=[]
    pending=None
    prev=None
    for raw in x:
        line=raw.rstrip('\n')
        if not line: continue
        if line.startswith('#EXT-X-BYTERANGE:'):
            n,at,o=line[17:].partition('@')
            if not n.isdigit() or (at and not o.isdigit()): return None
            pending=[int(n),int(o) if at else None]
        elif line.startswith('#'):
            continue
        else:
            if pending is None:
                out.append([line,None])
                prev=None
            else:
                n,o=pending
                if o is None:
                    if prev is None or prev[0]!=line: return None
                    o=prev[1]
                if n==0: return None
                out.append([line,'bytes=%d-%d'%(o,o+n-1)])
                prev=[line,o+n]
            pending=None
    return out
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
check('crlf-lines fixture 0', json.loads(json.dumps(solve(['#EXTM3U','#EXTINF:1,','#EXT-X-BYTERANGE:100@0','a.ts','#EXTINF:1,','#EXT-X-BYTERANGE:50','a.ts','#EXTINF:1,','#EXT-X-BYTERANGE:25','a.ts']))), json.loads(json.dumps([['a.ts','bytes=0-99'],['a.ts','bytes=100-149'],['a.ts','bytes=150-174']])))
check('crlf-lines fixture 1', json.loads(json.dumps(solve(['#EXTM3U','# generated by tool','#EXT-X-BYTERANGE:10@5','b.ts']))), json.loads(json.dumps([['b.ts','bytes=5-14']])))
check('crlf-lines fixture 2', json.loads(json.dumps(solve(['#EXT-X-BYTERANGE:10@0','a.ts','#EXT-X-BYTERANGE:10','b.ts']))), json.loads(json.dumps(None)))
check('crlf-lines fixture 3', json.loads(json.dumps(solve(['#EXT-X-BYTERANGE:10@0','a.ts?v=1','#EXT-X-BYTERANGE:10','a.ts?v=2']))), json.loads(json.dumps(None)))
check('crlf-lines fixture 4', json.loads(json.dumps(solve(['#EXT-X-BYTERANGE:10','a.ts']))), json.loads(json.dumps(None)))
check('crlf-lines fixture 5', json.loads(json.dumps(solve(['full.ts','#EXT-X-BYTERANGE:10','a.ts']))), json.loads(json.dumps(None)))
check('crlf-lines fixture 6', json.loads(json.dumps(solve(['#EXT-X-BYTERANGE:10@0','a.ts','a.ts','#EXT-X-BYTERANGE:10','a.ts']))), json.loads(json.dumps(None)))
check('crlf-lines fixture 7', json.loads(json.dumps(solve(['#EXT-X-BYTERANGE:10@0','a.ts','b.ts']))), json.loads(json.dumps([['a.ts','bytes=0-9'],['b.ts',None]])))
check('crlf-lines fixture 8', json.loads(json.dumps(solve(['#EXT-X-BYTERANGE:0@0','a.ts']))), json.loads(json.dumps(None)))
check('crlf-lines fixture 9', json.loads(json.dumps(solve(['#EXT-X-BYTERANGE:5@0','a.ts','#EXT-X-BYTERANGE:0','a.ts']))), json.loads(json.dumps(None)))
check('crlf-lines fixture 10', json.loads(json.dumps(solve(['#EXTM3U\r\n','#EXT-X-BYTERANGE:10@0\r\n','a.ts\r\n','#EXT-X-BYTERANGE:4\r\n','a.ts\r\n']))), json.loads(json.dumps([['a.ts','bytes=0-9'],['a.ts','bytes=10-13']])))
check('crlf-lines fixture 11', json.loads(json.dumps(solve(['#EXT-X-BYTERANGE:1x@0','a.ts']))), json.loads(json.dumps(None)))
check('crlf-lines fixture 12', json.loads(json.dumps(solve(['#EXT-X-BYTERANGE:10@','a.ts']))), json.loads(json.dumps(None)))
check('crlf-lines fixture 13', json.loads(json.dumps(solve(['','#EXT-X-BYTERANGE:3@7','   ','c.ts']))), json.loads(json.dumps([['c.ts','bytes=7-9']])))
check('crlf-lines fixture 14', json.loads(json.dumps(solve(["#EXT-X-BYTERANGE:%d@0"%N,'a.ts',"#EXT-X-BYTERANGE:%d"%N,'a.ts']))), json.loads(json.dumps([['a.ts',"bytes=0-%d"%(N-1)],['a.ts',"bytes=%d-%d"%(N,2*N-1)]])))
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 fixtureActualExpectedOutcome
crlf-lines fixture 0[['a.ts', 'bytes=0-99'], ['a.ts', 'bytes=100-149'], ['a.ts', 'bytes=150-174']][['a.ts', 'bytes=0-99'], ['a.ts', 'bytes=100-149'], ['a.ts', 'bytes=150-174']]Passed
crlf-lines fixture 1[['b.ts', 'bytes=5-14']][['b.ts', 'bytes=5-14']]Passed
crlf-lines fixture 2NoneNonePassed
crlf-lines fixture 3NoneNonePassed
crlf-lines fixture 4NoneNonePassed
crlf-lines fixture 5NoneNonePassed
crlf-lines fixture 6NoneNonePassed
crlf-lines fixture 7[['a.ts', 'bytes=0-9'], ['b.ts', None]][['a.ts', 'bytes=0-9'], ['b.ts', None]]Passed
crlf-lines fixture 8NoneNonePassed
crlf-lines fixture 9NoneNonePassed
crlf-lines fixture 10None[['a.ts', 'bytes=0-9'], ['a.ts', 'bytes=10-13']]Failed
crlf-lines fixture 11NoneNonePassed
crlf-lines fixture 12NoneNonePassed
crlf-lines fixture 13[[' ', 'bytes=7-9'], ['c.ts', None]][['c.ts', 'bytes=7-9']]Failed
crlf-lines fixture 14[['a.ts', 'bytes=0-0'], ['a.ts', 'bytes=1-1']][['a.ts', 'bytes=0-0'], ['a.ts', 'bytes=1-1']]Passed

SHA-256 / d8848239d7e55657927420020f57cf0796f65849f8e41cf365bb3bc08db4682f

3 / The verified repair

Exit 0
"""Failure Map reference implementation. Python standard library only."""
import json

N = 1
observations = []
def solve(x):
    out=[]
    pending=None
    prev=None
    for raw in x:
        line=raw.strip()
        if not line: continue
        if line.startswith('#EXT-X-BYTERANGE:'):
            n,at,o=line[17:].partition('@')
            if not n.isdigit() or (at and not o.isdigit()): return None
            pending=[int(n),int(o) if at else None]
        elif line.startswith('#'):
            continue
        else:
            if pending is None:
                out.append([line,None])
                prev=None
            else:
                n,o=pending
                if o is None:
                    if prev is None or prev[0]!=line: return None
                    o=prev[1]
                if n==0: return None
                out.append([line,'bytes=%d-%d'%(o,o+n-1)])
                prev=[line,o+n]
            pending=None
    return out
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
check('crlf-lines fixture 0', json.loads(json.dumps(solve(['#EXTM3U','#EXTINF:1,','#EXT-X-BYTERANGE:100@0','a.ts','#EXTINF:1,','#EXT-X-BYTERANGE:50','a.ts','#EXTINF:1,','#EXT-X-BYTERANGE:25','a.ts']))), json.loads(json.dumps([['a.ts','bytes=0-99'],['a.ts','bytes=100-149'],['a.ts','bytes=150-174']])))
check('crlf-lines fixture 1', json.loads(json.dumps(solve(['#EXTM3U','# generated by tool','#EXT-X-BYTERANGE:10@5','b.ts']))), json.loads(json.dumps([['b.ts','bytes=5-14']])))
check('crlf-lines fixture 2', json.loads(json.dumps(solve(['#EXT-X-BYTERANGE:10@0','a.ts','#EXT-X-BYTERANGE:10','b.ts']))), json.loads(json.dumps(None)))
check('crlf-lines fixture 3', json.loads(json.dumps(solve(['#EXT-X-BYTERANGE:10@0','a.ts?v=1','#EXT-X-BYTERANGE:10','a.ts?v=2']))), json.loads(json.dumps(None)))
check('crlf-lines fixture 4', json.loads(json.dumps(solve(['#EXT-X-BYTERANGE:10','a.ts']))), json.loads(json.dumps(None)))
check('crlf-lines fixture 5', json.loads(json.dumps(solve(['full.ts','#EXT-X-BYTERANGE:10','a.ts']))), json.loads(json.dumps(None)))
check('crlf-lines fixture 6', json.loads(json.dumps(solve(['#EXT-X-BYTERANGE:10@0','a.ts','a.ts','#EXT-X-BYTERANGE:10','a.ts']))), json.loads(json.dumps(None)))
check('crlf-lines fixture 7', json.loads(json.dumps(solve(['#EXT-X-BYTERANGE:10@0','a.ts','b.ts']))), json.loads(json.dumps([['a.ts','bytes=0-9'],['b.ts',None]])))
check('crlf-lines fixture 8', json.loads(json.dumps(solve(['#EXT-X-BYTERANGE:0@0','a.ts']))), json.loads(json.dumps(None)))
check('crlf-lines fixture 9', json.loads(json.dumps(solve(['#EXT-X-BYTERANGE:5@0','a.ts','#EXT-X-BYTERANGE:0','a.ts']))), json.loads(json.dumps(None)))
check('crlf-lines fixture 10', json.loads(json.dumps(solve(['#EXTM3U\r\n','#EXT-X-BYTERANGE:10@0\r\n','a.ts\r\n','#EXT-X-BYTERANGE:4\r\n','a.ts\r\n']))), json.loads(json.dumps([['a.ts','bytes=0-9'],['a.ts','bytes=10-13']])))
check('crlf-lines fixture 11', json.loads(json.dumps(solve(['#EXT-X-BYTERANGE:1x@0','a.ts']))), json.loads(json.dumps(None)))
check('crlf-lines fixture 12', json.loads(json.dumps(solve(['#EXT-X-BYTERANGE:10@','a.ts']))), json.loads(json.dumps(None)))
check('crlf-lines fixture 13', json.loads(json.dumps(solve(['','#EXT-X-BYTERANGE:3@7','   ','c.ts']))), json.loads(json.dumps([['c.ts','bytes=7-9']])))
check('crlf-lines fixture 14', json.loads(json.dumps(solve(["#EXT-X-BYTERANGE:%d@0"%N,'a.ts',"#EXT-X-BYTERANGE:%d"%N,'a.ts']))), json.loads(json.dumps([['a.ts',"bytes=0-%d"%(N-1)],['a.ts',"bytes=%d-%d"%(N,2*N-1)]])))
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 fixtureActualExpectedOutcome
crlf-lines fixture 0[['a.ts', 'bytes=0-99'], ['a.ts', 'bytes=100-149'], ['a.ts', 'bytes=150-174']][['a.ts', 'bytes=0-99'], ['a.ts', 'bytes=100-149'], ['a.ts', 'bytes=150-174']]Passed
crlf-lines fixture 1[['b.ts', 'bytes=5-14']][['b.ts', 'bytes=5-14']]Passed
crlf-lines fixture 2NoneNonePassed
crlf-lines fixture 3NoneNonePassed
crlf-lines fixture 4NoneNonePassed
crlf-lines fixture 5NoneNonePassed
crlf-lines fixture 6NoneNonePassed
crlf-lines fixture 7[['a.ts', 'bytes=0-9'], ['b.ts', None]][['a.ts', 'bytes=0-9'], ['b.ts', None]]Passed
crlf-lines fixture 8NoneNonePassed
crlf-lines fixture 9NoneNonePassed
crlf-lines fixture 10[['a.ts', 'bytes=0-9'], ['a.ts', 'bytes=10-13']][['a.ts', 'bytes=0-9'], ['a.ts', 'bytes=10-13']]Passed
crlf-lines fixture 11NoneNonePassed
crlf-lines fixture 12NoneNonePassed
crlf-lines fixture 13[['c.ts', 'bytes=7-9']][['c.ts', 'bytes=7-9']]Passed
crlf-lines fixture 14[['a.ts', 'bytes=0-0'], ['a.ts', 'bytes=1-1']][['a.ts', 'bytes=0-0'], ['a.ts', 'bytes=1-1']]Passed

SHA-256 / 94ce5a85b88cb0419461ff6f9801c9db1fe9b74b225dd3fcacd28756ad4bff72

Verification & scope

Deterministic simplified range service, with stipulated local policies and already parsed trusted inputs; not a complete HTTP implementation. 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:41:28.263291+00:00.

Case digest / e174e61e9616912d1b75aae548639c8785430e0d31373b73a3e33cfc21464fb8