Commit fcc4edf
Three defects, all reproduced on origin/main with a mock transport.
1. Automatic retry replayed writes. A POST that timed out was sent three
times, and so was a POST that got a 503:
POST timeout -> attempts: 3
POST 503 -> attempts: 3
GET timeout -> attempts: 3
A timeout or transport error is an AMBIGUOUS outcome, not a failure: the
server may have committed the write before the response was lost. A 5xx is
equally ambiguous, because a gateway can return 502 after the origin
committed. Subscription and webhook creates go through these helpers, so a
lost response became two subscriptions.
POST and PATCH are now sent exactly once. Idempotent methods (GET, HEAD,
OPTIONS, TRACE, PUT, DELETE) retry exactly as before. A 429 still retries
any method, because it is an outright refusal — the write definitively did
not happen. A caller who knows a write is safe to repeat can pass
idempotent=True to request(). When a write is not replayed the error says
so and carries .ambiguous_write = True, so the caller knows to check
whether it landed rather than blindly resending.
2. The constructor's `or` defaults discarded explicit configuration:
max_retries=0 -> 3 retry_on=[] -> [429, 500, 502, 503, 504]
Now explicit None checks. retry_on=[] is preserved and really does disable
status-code retries. max_retries counts total ATTEMPTS — which is what the
docstring always said and what `for attempt in range(self.max_retries)`
does — so it must be >= 1; 0, a negative, or a non-int raises
ConfigurationError naming the fix instead of silently becoming 3.
3. Retry-After was bounded above but not below:
Retry-After: 31612 -> waits [60.0, 60.0] (already capped)
Retry-After: -30 -> waits [-30.0, -30.0] (time.sleep raises ValueError)
Now clamped to [0, 60] through one shared RetryStrategy.bounded_wait(),
used by the sync client, request_with_headers() and the async client alike.
The 60s cap matters: the keyless demo returns retry-after 31612, 8.8 hours.
Durable-quota suppression (X-RateLimit-State: exhausted + a counter window) was
already correct on main and is unchanged; a regression test pins it.
RetryStrategy's public signatures are backward compatible: method/idempotent are
optional, and omitting them means "unknown", which is treated as replay-safe.
The SDK's own clients always pass the method.
Claude-Session: https://claude.ai/code/session_015ao5paex73xXvuM424Libo
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 parent b7017b7 commit fcc4edf
4 files changed
Lines changed: 608 additions & 36 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
44 | 44 | | |
45 | 45 | | |
46 | 46 | | |
47 | | - | |
| 47 | + | |
48 | 48 | | |
49 | 49 | | |
50 | 50 | | |
| |||
56 | 56 | | |
57 | 57 | | |
58 | 58 | | |
59 | | - | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
60 | 66 | | |
61 | 67 | | |
62 | 68 | | |
| |||
93 | 99 | | |
94 | 100 | | |
95 | 101 | | |
96 | | - | |
97 | | - | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
98 | 109 | | |
99 | 110 | | |
100 | 111 | | |
| |||
196 | 207 | | |
197 | 208 | | |
198 | 209 | | |
| 210 | + | |
199 | 211 | | |
200 | 212 | | |
201 | | - | |
| 213 | + | |
| 214 | + | |
| 215 | + | |
| 216 | + | |
| 217 | + | |
| 218 | + | |
| 219 | + | |
202 | 220 | | |
203 | 221 | | |
204 | 222 | | |
| |||
239 | 257 | | |
240 | 258 | | |
241 | 259 | | |
242 | | - | |
243 | | - | |
244 | | - | |
245 | | - | |
246 | | - | |
| 260 | + | |
| 261 | + | |
| 262 | + | |
| 263 | + | |
| 264 | + | |
| 265 | + | |
247 | 266 | | |
248 | 267 | | |
249 | 268 | | |
250 | 269 | | |
251 | 270 | | |
252 | 271 | | |
253 | 272 | | |
254 | | - | |
| 273 | + | |
| 274 | + | |
| 275 | + | |
| 276 | + | |
| 277 | + | |
255 | 278 | | |
256 | 279 | | |
257 | 280 | | |
| |||
273 | 296 | | |
274 | 297 | | |
275 | 298 | | |
276 | | - | |
| 299 | + | |
| 300 | + | |
| 301 | + | |
277 | 302 | | |
278 | 303 | | |
279 | 304 | | |
280 | 305 | | |
281 | 306 | | |
282 | 307 | | |
| 308 | + | |
| 309 | + | |
283 | 310 | | |
284 | 311 | | |
285 | 312 | | |
286 | 313 | | |
287 | 314 | | |
288 | 315 | | |
289 | | - | |
| 316 | + | |
| 317 | + | |
| 318 | + | |
290 | 319 | | |
291 | 320 | | |
292 | 321 | | |
| |||
296 | 325 | | |
297 | 326 | | |
298 | 327 | | |
| 328 | + | |
| 329 | + | |
299 | 330 | | |
300 | 331 | | |
301 | 332 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
45 | 45 | | |
46 | 46 | | |
47 | 47 | | |
48 | | - | |
| 48 | + | |
49 | 49 | | |
50 | 50 | | |
51 | 51 | | |
| |||
63 | 63 | | |
64 | 64 | | |
65 | 65 | | |
66 | | - | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
67 | 70 | | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
68 | 80 | | |
69 | 81 | | |
70 | 82 | | |
| |||
108 | 120 | | |
109 | 121 | | |
110 | 122 | | |
111 | | - | |
112 | | - | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
113 | 132 | | |
114 | 133 | | |
115 | 134 | | |
| |||
205 | 224 | | |
206 | 225 | | |
207 | 226 | | |
| 227 | + | |
208 | 228 | | |
209 | 229 | | |
210 | 230 | | |
| |||
218 | 238 | | |
219 | 239 | | |
220 | 240 | | |
| 241 | + | |
| 242 | + | |
| 243 | + | |
221 | 244 | | |
222 | 245 | | |
223 | 246 | | |
| |||
274 | 297 | | |
275 | 298 | | |
276 | 299 | | |
277 | | - | |
278 | | - | |
279 | | - | |
280 | | - | |
281 | | - | |
| 300 | + | |
| 301 | + | |
| 302 | + | |
| 303 | + | |
| 304 | + | |
| 305 | + | |
| 306 | + | |
| 307 | + | |
| 308 | + | |
282 | 309 | | |
283 | 310 | | |
284 | 311 | | |
285 | 312 | | |
286 | 313 | | |
287 | 314 | | |
288 | | - | |
| 315 | + | |
| 316 | + | |
| 317 | + | |
| 318 | + | |
| 319 | + | |
| 320 | + | |
| 321 | + | |
289 | 322 | | |
290 | 323 | | |
291 | 324 | | |
| |||
306 | 339 | | |
307 | 340 | | |
308 | 341 | | |
309 | | - | |
| 342 | + | |
| 343 | + | |
| 344 | + | |
310 | 345 | | |
311 | 346 | | |
312 | 347 | | |
313 | 348 | | |
314 | 349 | | |
315 | 350 | | |
| 351 | + | |
| 352 | + | |
316 | 353 | | |
317 | 354 | | |
318 | 355 | | |
319 | 356 | | |
320 | 357 | | |
321 | 358 | | |
322 | 359 | | |
323 | | - | |
| 360 | + | |
| 361 | + | |
| 362 | + | |
324 | 363 | | |
325 | 364 | | |
326 | 365 | | |
| |||
330 | 369 | | |
331 | 370 | | |
332 | 371 | | |
| 372 | + | |
| 373 | + | |
333 | 374 | | |
334 | 375 | | |
335 | 376 | | |
| |||
354 | 395 | | |
355 | 396 | | |
356 | 397 | | |
| 398 | + | |
357 | 399 | | |
358 | 400 | | |
359 | 401 | | |
| |||
388 | 430 | | |
389 | 431 | | |
390 | 432 | | |
391 | | - | |
392 | | - | |
393 | | - | |
394 | | - | |
395 | | - | |
| 433 | + | |
| 434 | + | |
| 435 | + | |
| 436 | + | |
| 437 | + | |
| 438 | + | |
| 439 | + | |
| 440 | + | |
| 441 | + | |
396 | 442 | | |
397 | 443 | | |
398 | 444 | | |
399 | 445 | | |
400 | 446 | | |
401 | 447 | | |
402 | | - | |
| 448 | + | |
| 449 | + | |
| 450 | + | |
| 451 | + | |
| 452 | + | |
| 453 | + | |
| 454 | + | |
403 | 455 | | |
404 | 456 | | |
405 | 457 | | |
| |||
420 | 472 | | |
421 | 473 | | |
422 | 474 | | |
423 | | - | |
| 475 | + | |
| 476 | + | |
| 477 | + | |
424 | 478 | | |
425 | 479 | | |
426 | 480 | | |
427 | 481 | | |
428 | 482 | | |
429 | 483 | | |
| 484 | + | |
| 485 | + | |
430 | 486 | | |
431 | 487 | | |
432 | 488 | | |
433 | 489 | | |
434 | 490 | | |
435 | 491 | | |
436 | | - | |
| 492 | + | |
| 493 | + | |
| 494 | + | |
437 | 495 | | |
438 | 496 | | |
439 | 497 | | |
| |||
443 | 501 | | |
444 | 502 | | |
445 | 503 | | |
| 504 | + | |
| 505 | + | |
446 | 506 | | |
447 | 507 | | |
448 | 508 | | |
| |||
0 commit comments