#13999·OmniRoute

logs/export: stream lacks mid-stream error handling, and the dashboard Export button silently truncates at 10k rows

Author: diegosouzapwCreated Sep 17, 2026Updated Sep 17, 2026
Labelsbug

Follow-up da #13428 (mergeada). O core dela resolve o OOM real do #13123; estes dois itens ficaram de fora e foram acordados como follow-up.

1. Erro no meio do stream entrega JSON truncado sem sinal

src/app/api/logs/export/route.ts monta um ReadableStream cujo start() não tem try/catch. Um erro de DB no meio da iteração (ou um getCallLogById lançando) rejeita o start() depois de 200 OK e Content-Disposition já terem ido para a rede. O cliente salva um .json truncado e não-parseável, sem nenhum sinal de erro. O try/catch externo da rota não cobre esse caminho.

Isso é falha silenciosa, que o AGENTS.md proíbe explicitamente para streams. Correção: controller.error() (ou fechar com um sentinel) dentro do start(), e logar.

2. count pode exceder logs.length

count vem de Math.min(totalAvailable, limit) a partir de um COUNT(*) separado, não das linhas realmente emitidas. iterateCallLogsSince pula ids cujo getCallLogById retorna null (if (log) yield log), então body.count pode ser maior que body.logs.length. Antes da #13428, count era literalmente rows.length.

3. Botão Export do dashboard trunca em silêncio

src/app/(dashboard)/dashboard/logs/page.tsx:62 chama /api/logs/export?hours=${hours}&type=${logType} sem limit, então cai no default de 10.000 linhas. O único chamador in-repo não foi atualizado e a UI nunca mostra o capped. Decisão: passar um limit maior e exibir o aviso de truncamento.

4. Paginação sobre tabela viva (documentar)

iterateProxyLogsSince usa LIMIT/OFFSET com ORDER BY timestamp DESC sobre uma tabela com inserção ativa — inserts concorrentes deslocam a janela e podem duplicar ou pular linhas entre páginas. Aceitável para export, mas não está documentado.

Crédito do trabalho original: @KooshaPari na #13428.