Error Classification, Circuit Breaker, Skipped Status, Progress Throttling
14/14 Tasks Complete 51 Tests Passing
FC-26 Stats Capture — Auto-Download System Improvements
Sprint Date: 2026-02-15 — Branch: feat/auto-download-phase1
Phase 1 delivers four capabilities that make the auto-download system self-protecting and operator-friendly.
Downloads errors are now classified into 4 categories: permanent, cookie_expired, rate_limited, and transient. Each category has a specific retry strategy.
A global circuit breaker trips after 3 consecutive cookie errors, stopping all downloads. Auto-resets after 30 minutes with a single test download before full resume.
Permanently unavailable videos are marked skipped instead of failed. Queryable via API, visible in UI, and reversible with a retry button.
Download progress DB writes reduced from every 1s to every 5s or 10% change threshold. Cuts DB churn by ~80% with 9 parallel downloads.
34 acceptance criteria across 8 areas. 33 passed, 1 deferred to Phase 2 by PO decision.
The classifyDownloadError() function maps yt-dlp error messages to categories using pattern matching, evaluated in priority order.
| File | Purpose |
|---|---|
backend/src/services/downloadErrorClassifier.ts | Error classification utility (4 categories, pattern matching) |
backend/src/services/downloadCircuitBreaker.ts | Global circuit breaker (trip/reset/decay/status) |
backend/src/services/progressThrottle.ts | Progress write throttle (5s / 10% threshold) |
backend/src/services/youtubeMonitorService.ts | Integration point (uses all 3 modules) |
backend/src/api/youtube/routes.ts | API endpoints (circuit-reset, retry, circuit-status) |
frontend/src/pages/operator/YouTubeMonitor.tsx | UI (circuit indicator, skipped filter, retry buttons) |
docs/operations/auto-download-runbook.md | Operator runbook |
| Test Suite | Tests | Duration | Status |
|---|---|---|---|
downloadErrorClassifier.unit.test.ts |
28 | 9ms | PASS |
progressThrottle.unit.test.ts |
23 | 6ms | PASS |
| Category | Tests | Coverage |
|---|---|---|
| Permanent (11 patterns) | 11 pattern tests + edge cases | All patterns verified |
| Cookie Expired (5 patterns) | 5 pattern tests + edge cases | All patterns verified |
| Rate Limited (3 patterns) | 3 pattern tests | All patterns verified |
| Transient (default) | Default fallback + unknown errors | Edge cases verified |
| shouldRetry flag | Per-category verification | All correct |
| Scenario | Tests |
|---|---|
| Time-based (5s interval) | Write after 5s, skip before 5s |
| Percentage-based (10% threshold) | Write at 10%+ jump, skip below |
| Combined (whichever first) | Both triggers interact correctly |
| Edge cases | 0%, 100%, no change, rapid changes |
New components added to the YouTube Monitor page (frontend/src/pages/operator/YouTubeMonitor.tsx).
Displayed in the operator toolbar. Three visual states:
Normal operation
Downloads blocked
Half-open, testing 1 download
Skipped videos use a violet/purple color scheme with a SkipForward icon, visually distinct from the red failed badge.
Appears when circuit breaker is open. Prominent call-to-action in the toolbar area.
Resets the circuit breaker and attempts a single test download on the next sync cycle.
When a download fails, check the error message in the feed item's detail view. The system has already classified it:
| You See | Category | What Happens | Your Action |
|---|---|---|---|
| "Video unavailable" / "private" | Skipped | Video marked skipped, won't retry | No action needed. Retry manually if video becomes available. |
| "Sign in to confirm" | Cookie | Circuit breaker counter increments | If circuit trips, refresh cookies and reset. |
| "429 Too Many Requests" | Rate Limit | Auto-retries next sync cycle | Usually self-resolves. Wait. |
| Network/server errors | Transient | Auto-retries next sync cycle | Check network if persistent. |
Notice the indicator — The toolbar shows "Circuit: OPEN" in red with a countdown timer.
Refresh your cookies — Export fresh cookies from your browser and copy youtube_cookies.txt to the server.
Click "Reset Circuit Breaker" — Or use POST /api/youtube/downloads/circuit-reset
Wait for test download — The system tries 1 video. If it works, full downloads resume automatically.
Filter by Skipped — Use the status filter dropdown and select "Skipped" to see all permanently skipped videos.
Review the reason — Each skipped video shows why it was skipped (e.g., "Video unavailable", "This video is private").
Retry if needed — Click the "Retry" button on any skipped video to re-queue it for download.
Download progress bars now update every ~5 seconds instead of every second. This is cosmetic — downloads run at the same speed. If progress appears stuck, the download is still running. Check the queue endpoint to confirm:
GET /api/youtube/queue
Full runbook at docs/operations/auto-download-runbook.md
| Scenario | Action |
|---|---|
| Circuit breaker tripped | Refresh cookies, then POST /api/youtube/downloads/circuit-reset |
| Circuit keeps tripping | Google OAuth expires every 7 days. Set weekly reminder. |
| Video stuck as skipped | Check availability, then POST /api/youtube/feed/<id>/retry |
| Many transient failures | Check network. Increase youtube_download_delay_seconds if persistent. |
| Rate limited (429) | Wait. Reduce youtube_max_parallel_downloads if persistent. |
| Progress seems stuck | Normal (5s updates). Check GET /api/youtube/queue |
| Check circuit status | GET /api/youtube/downloads/circuit-status |
| View skipped videos | GET /api/youtube/feed?status=skipped |
| Bulk retry failed | POST /api/youtube/feed/bulk-retry |
| Method | Endpoint | Auth | Purpose |
|---|---|---|---|
| GET | /api/youtube/downloads/circuit-status | User | Circuit breaker state |
| POST | /api/youtube/downloads/circuit-reset | Operator | Manual reset |
| GET | /api/youtube/feed?status=skipped | User | List skipped videos |
| POST | /api/youtube/feed/:videoId/retry | User | Retry single video |
| POST | /api/youtube/feed/bulk-retry | User | Retry all failed |
The bulk retry button reuses bulkRetryFailed() which only queries download_status = 'failed', not 'skipped'. Individual per-video retry works correctly. Fix: change Supabase filter to .in('download_status', ['failed', 'skipped']).
Circuit breaker state is in-memory only. If the server restarts, the circuit breaker resets to closed. DB persistence planned for Phase 2 (Task #15 in backlog).
Harare, February 2026 — The FC-26 Stats Capture platform today announced Phase 1 of its auto-download system improvements, introducing intelligent error classification and a circuit breaker pattern that automatically protects operator sessions from YouTube bot detection.
Previously, when YouTube detected bot-like behavior and invalidated session cookies, the auto-download system would continue attempting downloads for every queued video — wasting compute, generating log noise, and burning through cookie sessions faster. Operators discovered the problem minutes or hours later via a wall of failed downloads.
With Phase 1, the system now classifies every download error into four categories and takes smart action for each. Permanently unavailable videos are quietly marked as "skipped" rather than endlessly retried. Cookie errors trigger a global circuit breaker after just 3 consecutive failures, immediately halting all downloads and alerting the operator.
Key capabilities:
• Smart Error Classification — 4 categories (permanent, cookie, rate-limit, transient) with automatic retry decisions
• Circuit Breaker — Stops all downloads after 3 cookie errors, auto-resets after 30 minutes with a test download
• Skipped Status — Permanently unavailable videos are queryable and individually retryable
• 80% DB Write Reduction — Progress throttling cuts database churn from 9 writes/second to ~1.8
"The system should stop itself before the operator has to," said the development team. "Phase 1 makes the auto-download system self-aware about the most common failure mode — cookie burn — and responds in seconds, not hours."
cookie_expired. After 3 consecutive cookie errors, the circuit breaker trips, immediately stopping all downloads. The operator sees a red "Circuit: OPEN" indicator with a countdown timer. Downloads resume automatically after 30 minutes (with a test download) or immediately after the operator refreshes cookies and clicks "Reset Circuit Breaker."skipped with a violet badge. Skipped videos are never auto-retried but can be manually retried if the video becomes available again.queued and it will be picked up on the next sync cycle. You can also use the API: POST /api/youtube/feed/:videoId/retry.POST /api/youtube/downloads/circuit-reset. Always refresh your cookies first — resetting without fresh cookies will just cause the circuit breaker to trip again after 3 more failures.| # | Role | Key Deliverable |
|---|---|---|
| 1 | Product Owner | 34 acceptance criteria, PRFAQ |
| 2 | Scrum Master | Sprint coordination, dependency tracking |
| 3 | Backend Developer | Error classifier, circuit breaker, skipped status, progress throttle |
| 4 | Frontend Developer | Circuit breaker indicator, skipped filter, retry buttons |
| 5 | UI Designer | Component design specs (3 states, violet palette) |
| 6 | UX Researcher | Operator workflow validation |
| 7 | QA Engineer | 51 unit tests (28 classifier + 23 throttle) |
| 8 | DevOps Engineer | DB migration verification, API endpoint validation |
| 9 | Business Analyst | Operator runbook, user guide, PRFAQ |
| 10 | Code Reviewer | Triple-gate review, bulk retry bug discovery |