Scan queue and retries
How Pranas processes scans in the background and automatically retries transient failures.
Last updated: 2026-05-05
TL;DR — Pranas runs scans through a database-backed queue so they survive page refreshes, browser crashes, and tab closes. Failed queries are automatically retried once with a doubled timeout — you never need to press retry manually. A small per-user concurrency cap keeps shared infrastructure responsive for everyone.
Why a queue exists
AI Scans can take seconds (small scan, fast model) to 15+ minutes (large scan, slow premium model). If execution lived in your browser tab, closing the tab or losing wifi would orphan the scan. Worse, if many users launched big scans at the same time, edge functions would time out and everyone's scans would fail.
The queue solves both problems:
- Persistence — every (prompt × model) execution is a row in the database. Workers pick them up and process them. Your browser is just a control surface; the actual work happens server-side.
- Fairness — a per-user concurrency cap prevents any single account from monopolizing capacity and starving other users.
- Reliability — automatic retry recovers transient model errors (rate limits, network blips, brief outages) without your involvement.
How a scan flows through the queue
1. You click "Run Scan"
↓
2. Pranas inserts N rows into the queue (one per prompt × model)
with status = "pending"
↓
3. A background worker picks up "pending" rows in batches
(batch size depends on model weight — see Model tiering)
↓
4. For each row: call the model, capture response, parse brands
and sources, mark "completed"
↓
5. If a row times out or errors: mark "failed_retryable",
re-insert as "pending" with doubled timeout (one retry)
↓
6. If retry also fails: mark "failed" permanently
↓
7. Once all rows for the scan are terminal, the scan summary
recomputes and the dashboard refreshes
Steps 3–7 happen entirely server-side. You can close the browser, reload, or navigate away — the scan continues.
Auto-retry behavior
Pranas retries each failed query exactly once, automatically, with a 2× timeout on the second attempt. The retry is enqueued as a fresh pending row, not a direct re-call from the client (see Retry compliance for why this matters for billing accuracy).
What auto-retry catches:
- Transient rate limits (429s)
- Brief network blips between Pranas and OpenRouter
- Model overload (5xx responses from the provider)
- Soft timeouts on slower models
What auto-retry doesn't catch:
- Hard model errors (e.g. content-policy blocks)
- Persistent provider outages
- Unparseable responses from quirky models (e.g. DeepSeek R1 reasoning streams)
For these, the row is marked failed after the second attempt and the scan summary will list the failure count.
Per-user fairness
Each user has a concurrent execution cap that limits how many of their queue rows run at the same time. This:
- Prevents one user from saturating shared model quotas
- Keeps edge-function utilization within Supabase's safe envelope
- Smooths cost spikes
If you launch a large scan, you'll see steady progress over a few minutes rather than a giant burst. This is by design — the total time is the same; the load profile is gentler.
Model tiering and batch sizes
Different models have different speed and reliability profiles. Pranas tunes batch size and timeout per model:
- Fast/cheap models (GPT-5 nano, Gemini Flash Lite): larger batches, shorter timeouts.
- Heavy reasoning models (GPT-5, Gemini 3 Pro, Claude): smaller batches, longer timeouts.
- Quirky models (DeepSeek R1 with extended reasoning): special handling to avoid spurious failures.
You don't configure this — the queue picks the right settings automatically based on which models you selected.
Worked example
Maya runs a 30-prompt × 4-model scan = 120 queue rows. She sees:
- T+0s: scan launches, 120 rows enter the queue, status = "pending".
- T+10s: 12 rows already "completed" (fast Gemini Flash Lite finished first).
- T+45s: 60 rows "completed", 4 marked "failed_retryable" (transient rate limits on GPT-5).
- T+1m 30s: those 4 rows retried, 3 succeeded, 1 still failing.
- T+3m 20s: all 120 rows terminal. Scan summary: 117 completed, 2 "no brands found", 1 failed. Dashboard refreshes.
Maya never had to intervene. She closed her laptop at T+45s and came back to a finished scan.
Edge cases & gotchas
- You don't need to "press retry" — it's automatic. A "Retry" button only appears for rows that have failed both attempts and need a fresh manual queue entry.
- Closing the browser doesn't stop the scan — execution is server-side.
- You can launch multiple scans in a row — they queue up and process in order, subject to your concurrency cap.
- Failed retries are not double-billed — the queue tracks attempts and bills only successful or unrecoverable executions per the credit policy.
- Very large scans (300+ executions) finish in waves. Don't be alarmed if progress pauses briefly — that's the worker batching the next chunk.
Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
| Scan stuck in "Processing" >30 min | Model outage; queue waiting | Check status page; if persistent, contact support with scan ID |
| All queries failed | Email unverified, no credits, or full provider outage | Verify email, top up, retry later |
| Progress bar frozen | UI cache, queue still moving | Hard refresh; the underlying scan is unaffected |
| Same query failed twice | Persistent error (content policy, model removed) | Adjust the prompt or remove that model and re-run |
Related articles
- What is an AI Scan?
- Scan statuses explained
- Credits per scan
- Choose AI models for a scan
- Scan failures
Still need help?
Email contact@pranas.co with the scan ID — we can inspect the queue and tell you exactly which rows failed and why.