FAILURE MAP
← Case archive

FA-29841 / HTTP retries / Open access

Expect-Continue request retry phases: Final stops violates retry transition semantics · case 01

Final stops violates retry transition semantics

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

ROOT CAUSE

Final stops violates retry transition semantics

VERIFIED REPAIR

Restore the specified transition phase='done'; out.append.

Unsuccessful approach: The attempted repair changes the faulty site to phase='waiting'; out.append but still violates a regression oracle.

Case contract

start(body size) starts awaiting permission. interim100 permits sending; other interim messages do not. tick permits optimistic sending after wait expiry. send(bytes) emits at most remaining body only in permitted phase. final(status) stops body transmission and records response. fail permits retry with the same full body only if zero body bytes were sent; otherwise ambiguous. retry resets the handshaking phase after retry permission. Return transmissions and decisions.

Why this case matters

Offline deterministic model of HTTP request retries.

1 / The failure

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

N = 1
observations = []
def solve(events):
    size=0; sent=0; phase='idle'; out=[]
    for e in events:
        if e[0]=='start':
            size=e[1]; sent=0; phase='waiting'
        elif e[0]=='interim':
            if e[1]==100 and phase=='waiting': phase='allowed'
        elif e[0]=='tick':
            if phase=='waiting': phase='allowed'
        elif e[0]=='send':
            if phase!='allowed': out.append('blocked'); continue
            count=min(e[1],size-sent)
            sent+=count
            out.append(count)
        elif e[0]=='final':
            phase='allowed'; out.append(['response',e[1]])
        elif e[0]=='fail':
            if phase=='done': out.append('terminal'); continue
            phase='retry' if sent==0 else 'ambiguous'
            out.append(phase)
        elif e[0]=='retry':
            if phase!='retry': out.append('denied'); continue
            sent=0; phase='waiting'; out.append('restarted')
    return [out,sent]
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
check('0', solve([]), [[],0])
check('1', solve([('start',N+3),('send',1),('interim',103),('send',1),('interim',100),('send',2),('send',N+3)]), [['blocked','blocked',2,N+1],N+3])
check('2', solve([('start',N),('fail',),('retry',),('send',1),('tick',),('send',N)]), [['retry','restarted','blocked',N],N])
check('3', solve([('start',2),('tick',),('send',1),('fail',),('retry',)]), [[1,'ambiguous','denied'],1])
check('4', solve([('start',N),('final',417),('interim',100),('tick',),('send',1),('fail',),('retry',)]), [[['response',417],'blocked','terminal','denied'],0])
check('5', solve([('start',2),('tick',),('send',2),('start',N),('fail',)]), [[2,'retry'],0])
check('6', solve([('start',0),('interim',100),('send',3),('send',0)]), [[0,0],0])
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
0[[], 0][[], 0]Passed
1[['blocked', 'blocked', 2, 2], 4][['blocked', 'blocked', 2, 2], 4]Passed
2[['retry', 'restarted', 'blocked', 1], 1][['retry', 'restarted', 'blocked', 1], 1]Passed
3[[1, 'ambiguous', 'denied'], 1][[1, 'ambiguous', 'denied'], 1]Passed
4[[['response', 417], 1, 'ambiguous', 'denied'], 1][[['response', 417], 'blocked', 'terminal', 'denied'], 0]Failed
5[[2, 'retry'], 0][[2, 'retry'], 0]Passed
6[[0, 0], 0][[0, 0], 0]Passed

SHA-256 / a63c6a702dfc4396f3995c571a0c5a053ea70bacf64f505093ff9841e79534ea

2 / The unsuccessful fix

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

N = 1
observations = []
def solve(events):
    size=0; sent=0; phase='idle'; out=[]
    for e in events:
        if e[0]=='start':
            size=e[1]; sent=0; phase='waiting'
        elif e[0]=='interim':
            if e[1]==100 and phase=='waiting': phase='allowed'
        elif e[0]=='tick':
            if phase=='waiting': phase='allowed'
        elif e[0]=='send':
            if phase!='allowed': out.append('blocked'); continue
            count=min(e[1],size-sent)
            sent+=count
            out.append(count)
        elif e[0]=='final':
            phase='waiting'; out.append(['response',e[1]])
        elif e[0]=='fail':
            if phase=='done': out.append('terminal'); continue
            phase='retry' if sent==0 else 'ambiguous'
            out.append(phase)
        elif e[0]=='retry':
            if phase!='retry': out.append('denied'); continue
            sent=0; phase='waiting'; out.append('restarted')
    return [out,sent]
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
check('0', solve([]), [[],0])
check('1', solve([('start',N+3),('send',1),('interim',103),('send',1),('interim',100),('send',2),('send',N+3)]), [['blocked','blocked',2,N+1],N+3])
check('2', solve([('start',N),('fail',),('retry',),('send',1),('tick',),('send',N)]), [['retry','restarted','blocked',N],N])
check('3', solve([('start',2),('tick',),('send',1),('fail',),('retry',)]), [[1,'ambiguous','denied'],1])
check('4', solve([('start',N),('final',417),('interim',100),('tick',),('send',1),('fail',),('retry',)]), [[['response',417],'blocked','terminal','denied'],0])
check('5', solve([('start',2),('tick',),('send',2),('start',N),('fail',)]), [[2,'retry'],0])
check('6', solve([('start',0),('interim',100),('send',3),('send',0)]), [[0,0],0])
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
0[[], 0][[], 0]Passed
1[['blocked', 'blocked', 2, 2], 4][['blocked', 'blocked', 2, 2], 4]Passed
2[['retry', 'restarted', 'blocked', 1], 1][['retry', 'restarted', 'blocked', 1], 1]Passed
3[[1, 'ambiguous', 'denied'], 1][[1, 'ambiguous', 'denied'], 1]Passed
4[[['response', 417], 1, 'ambiguous', 'denied'], 1][[['response', 417], 'blocked', 'terminal', 'denied'], 0]Failed
5[[2, 'retry'], 0][[2, 'retry'], 0]Passed
6[[0, 0], 0][[0, 0], 0]Passed

SHA-256 / ed2f2b7395959a6da93e07b3d0b64601f261c75c61efc2eb3232e46664d453c6

3 / The verified repair

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

N = 1
observations = []
def solve(events):
    size=0; sent=0; phase='idle'; out=[]
    for e in events:
        if e[0]=='start':
            size=e[1]; sent=0; phase='waiting'
        elif e[0]=='interim':
            if e[1]==100 and phase=='waiting': phase='allowed'
        elif e[0]=='tick':
            if phase=='waiting': phase='allowed'
        elif e[0]=='send':
            if phase!='allowed': out.append('blocked'); continue
            count=min(e[1],size-sent)
            sent+=count
            out.append(count)
        elif e[0]=='final':
            phase='done'; out.append(['response',e[1]])
        elif e[0]=='fail':
            if phase=='done': out.append('terminal'); continue
            phase='retry' if sent==0 else 'ambiguous'
            out.append(phase)
        elif e[0]=='retry':
            if phase!='retry': out.append('denied'); continue
            sent=0; phase='waiting'; out.append('restarted')
    return [out,sent]
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
check('0', solve([]), [[],0])
check('1', solve([('start',N+3),('send',1),('interim',103),('send',1),('interim',100),('send',2),('send',N+3)]), [['blocked','blocked',2,N+1],N+3])
check('2', solve([('start',N),('fail',),('retry',),('send',1),('tick',),('send',N)]), [['retry','restarted','blocked',N],N])
check('3', solve([('start',2),('tick',),('send',1),('fail',),('retry',)]), [[1,'ambiguous','denied'],1])
check('4', solve([('start',N),('final',417),('interim',100),('tick',),('send',1),('fail',),('retry',)]), [[['response',417],'blocked','terminal','denied'],0])
check('5', solve([('start',2),('tick',),('send',2),('start',N),('fail',)]), [[2,'retry'],0])
check('6', solve([('start',0),('interim',100),('send',3),('send',0)]), [[0,0],0])
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
0[[], 0][[], 0]Passed
1[['blocked', 'blocked', 2, 2], 4][['blocked', 'blocked', 2, 2], 4]Passed
2[['retry', 'restarted', 'blocked', 1], 1][['retry', 'restarted', 'blocked', 1], 1]Passed
3[[1, 'ambiguous', 'denied'], 1][[1, 'ambiguous', 'denied'], 1]Passed
4[[['response', 417], 'blocked', 'terminal', 'denied'], 0][[['response', 417], 'blocked', 'terminal', 'denied'], 0]Passed
5[[2, 'retry'], 0][[2, 'retry'], 0]Passed
6[[0, 0], 0][[0, 0], 0]Passed

SHA-256 / d2c68d02cac79d5eb2dc982d5748b4af117a473da79c899f2ce3c5898a4b510a

Verification & scope

Stipulated bounded simulator, not a complete HTTP implementation or a standards conformance claim. 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:47.144161+00:00.

Case digest / 8f003665bab830f9b5425f12da7f1afda379da5d71b8c679a999bd3022a822e4