FA-29821 / HTTP retries / Open access
Expect-Continue request retry phases: Wait expiry violates retry transition semantics · case 01
Wait expiry violates retry transition semantics
ROOT CAUSE
Wait expiry violates retry transition semantics
VERIFIED REPAIR
Restore the specified transition if phase=='waiting': phase='allowed'.
Unsuccessful approach: The attempted repair changes the faulty site to phase='allowed' 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=='idle': 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 fixture | Actual | Expected | Outcome |
|---|---|---|---|
| 0 | [[], 0] | [[], 0] | Passed |
| 1 | [['blocked', 'blocked', 2, 2], 4] | [['blocked', 'blocked', 2, 2], 4] | Passed |
| 2 | [['retry', 'restarted', 'blocked', 'blocked'], 0] | [['retry', 'restarted', 'blocked', 1], 1] | Failed |
| 3 | [['blocked', 'retry', 'restarted'], 0] | [[1, 'ambiguous', 'denied'], 1] | Failed |
| 4 | [[['response', 417], 'blocked', 'terminal', 'denied'], 0] | [[['response', 417], 'blocked', 'terminal', 'denied'], 0] | Passed |
| 5 | [['blocked', 'retry'], 0] | [[2, 'retry'], 0] | Failed |
| 6 | [[0, 0], 0] | [[0, 0], 0] | Passed |
SHA-256 / 67c4a8700f23a40a4ae107cb16fdcc7352c9cb77a3ba9a5691eb9df4d2cf962f
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':
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 fixture | Actual | Expected | Outcome |
|---|---|---|---|
| 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 / 9733e443903ff7a25c18416c9468cb6cb49939d53f7ebfa3e0c551f9b668020c
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 fixture | Actual | Expected | Outcome |
|---|---|---|---|
| 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:46.915714+00:00.
Case digest / c9bbd015b7660ddb8c9770e68f2b6e1f9ef3f3becfa431a1fe3c0eb024845f4e