{"slug": "your-retry-logic-has-never-seen-a-real-429-here-is-how-to-test-it-with-a-local", "title": "Your retry logic has never seen a real 429. Here is how to test it with a local fault-injecting mock", "summary": "A developer published a small local HTTP server that mimics an LLM chat-completions endpoint and returns a configurable failure schedule, letting engineers exercise retry branches for 429s, 500s and timeouts that normally go untested until production. The roughly 25-line Python script reads a JSON schedule, consumes one step per request, emits a Retry-After header on 429s and sleeps to simulate a hang, and is pointed at by changing the client's base URL to localhost. A more complete version with schedule files, a README and examples is offered as a pay-what-you-want download starting at $1.", "body_md": "Most LLM client code has a retry branch that has never actually run. The provider returns 200 in every test, so the `429`, `500` and timeout paths sit untested until production hits them at 3am.\n\nThe fix is cheap: point your client at a local server that fails on a schedule you control.\n\nRun a tiny HTTP server on localhost that mimics a chat-completions endpoint. Give it a failure schedule, for example:\n\n```\n[\n  {\"status\": 429, \"retry_after\": 2},\n  {\"status\": 500},\n  {\"status\": \"timeout\"},\n  {\"status\": 200}\n]\n```\n\nEach incoming request consumes the next step. Your client sees a 429 with a `Retry-After` header, then a 500, then a hang, then a good response.\n\n``` python\nimport json, sys, time\nfrom http.server import BaseHTTPRequestHandler, HTTPServer\n\nSCHEDULE = json.load(open(sys.argv[1]))\nstep = {\"i\": 0}\n\nclass H(BaseHTTPRequestHandler):\n    def do_POST(self):\n        s = SCHEDULE[min(step[\"i\"], len(SCHEDULE) - 1)]\n        step[\"i\"] += 1\n        if s[\"status\"] == \"timeout\":\n            time.sleep(60)\n            return\n        self.send_response(s[\"status\"])\n        if \"retry_after\" in s:\n            self.send_header(\"Retry-After\", str(s[\"retry_after\"]))\n        self.end_headers()\n        self.wfile.write(b'{\"choices\":[{\"message\":{\"content\":\"ok\"}}]}')\n\nHTTPServer((\"127.0.0.1\", 9797), H).serve_forever()\n```\n\nPoint your client's base URL at `http://127.0.0.1:9797` and run your normal code path.\n\nI bundled a slightly more complete version (schedule file, README, examples) as a pay-what-you-want download, minimum $1: [https://renevibe76.gumroad.com/l/sargxm](https://renevibe76.gumroad.com/l/sargxm)\n\nThe snippet above is enough to get started on your own, though. The point is to see your retry code run at least once before production does it for you.", "url": "https://wpnews.pro/news/your-retry-logic-has-never-seen-a-real-429-here-is-how-to-test-it-with-a-local", "canonical_source": "https://dev.to/renev3408/your-retry-logic-has-never-seen-a-real-429-here-is-how-to-test-it-with-a-local-fault-injecting-mock-2pi2", "published_at": "2026-10-17 03:16:59+00:00", "updated_at": "2026-10-17 03:27:55.100317+00:00", "lang": "en", "topics": ["ai-tools", "developer-tools", "ai-agents"], "entities": ["Gumroad"], "also_reported_by": [], "alternates": {"html": "https://wpnews.pro/news/your-retry-logic-has-never-seen-a-real-429-here-is-how-to-test-it-with-a-local", "markdown": "https://wpnews.pro/news/your-retry-logic-has-never-seen-a-real-429-here-is-how-to-test-it-with-a-local.md", "text": "https://wpnews.pro/news/your-retry-logic-has-never-seen-a-real-429-here-is-how-to-test-it-with-a-local.txt", "jsonld": "https://wpnews.pro/news/your-retry-logic-has-never-seen-a-real-429-here-is-how-to-test-it-with-a-local.jsonld"}}