Production Hardening an AI Video Pipeline: Retries, Fallbacks, and Crash Guards An engineer at a video generation platform detailed five reliability fixes totaling roughly 1,200 lines of code that hardened an AI pipeline against provider outages, unhandled promise rejections, and null field crashes. The changes included an error classifier with exponential backoff for transient failures, a two-tier model fallback for Claude calls, and crash guards for the Next.js server. The work addressed failures where a ten-minute backend outage exhausted all retries and a null optional field from Claude crashed plan validation. The video generation platform I work on orchestrates a long chain of AI calls — a video generation provider for clip rendering, Claude for scene planning, ElevenLabs for voiceover — into finished ad creatives. When every provider was healthy, the pipeline worked. When they were not, it failed quietly: a ten-minute backend outage burned through all retries, an unhandled promise rejection took down the Next.js server with a 502 and no stack trace, and a null optional field from Claude crashed plan validation before a single clip was generated. These were reliability gaps, not creative logic bugs. I addressed them across five focused pull requests totaling roughly 1,200 lines. This post walks through each one. The first incident was blunt. During a live run, the video generation provider's backend went down for roughly ten minutes. All sixteen clips in the job failed. The retry logic gave up long before the outage ended. The old configuration was simple and wrong for provider-scale outages: three retries with a fixed fifteen-second delay between attempts. That is a forty-five-second horizon. A transient backend incident routinely lasts five to fifteen minutes. Retrying three times and declaring failure is not resilience — it is giving up on the first long tail. I built an error classifier that separates transient failures from permanent ones. Transient errors — internal server errors, "try again later" messages, rate limits, capacity or overload signals, timeouts, HTTP 5xx, and 429 responses — get retried with exponential backoff. Non-transient errors — bad input, authentication failures, invalid parameters — fail immediately. There is no point burning retry budget on a request that will never succeed. js const TRANSIENT PATTERNS = /internal error/i, /try again later/i, /rate.?limit/i, /capacity|overload/i, /timeout/i, ; function isTransientProviderError msg: string, status?: number : boolean { if status === 429 || status == undefined && status = 500 return true; return TRANSIENT PATTERNS.some re = re.test msg ; } const BACKOFF MS = 15 000, 30 000, 60 000, 120 000 ; async function generateClipWithRetry request: ClipRequest, maxAttempts = BACKOFF MS.length, : Promise