fix: FormData stream exhausted on retry + undefined segments crash
All checks were successful
Build & Push Docker Image / test (push) Successful in 32s
Build & Push Docker Image / build-and-push (push) Successful in 46s

Two bugs triggered together when the model was unloaded during a job:

1. submitJob() created FormData/createReadStream once outside the retry loop.
   After a 503, the audio ReadStream was consumed and subsequent retries sent
   an empty body to whisper, causing it to return segments:undefined.

2. webhook handler cast whisperJob.segments as Segment[] without guarding
   against undefined, so deduplicateSegments(undefined) crashed with
   'Cannot read properties of undefined (reading 'map')' — stored as job.error.

Fixes:
- Move FormData + createReadStream inside the retry loop (fresh stream per attempt)
- Use (whisperJob.segments ?? []) in webhook handler
- Add Array.isArray guard at top of deduplicateSegments() as belt-and-suspenders

Tests:
- New: verifies createReadStream called once per attempt (3 attempts = 3 streams)
- New: webhook handles segments:undefined without throwing
- New: webhook handles segments:null without throwing
- 150/150 passing

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
Giancarmine Salucci
2026-05-10 15:37:07 +02:00
parent a76625d378
commit 10a3669b42
5 changed files with 56 additions and 9 deletions

View File

@@ -90,13 +90,16 @@ export async function submitJob(
const { createReadStream } = await import('fs');
const { default: fetch } = await import('node-fetch');
const form = new FormData();
form.append('audio', createReadStream(wavPath));
form.append('task', 'transcribe');
form.append('webhook_url', webhookUrl);
if (language) form.append('language', language);
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
// Recreate form with a fresh readable stream on every attempt.
// A consumed ReadStream cannot be rewound, so reusing it across retries
// would send an empty body to whisper after the first 503.
const form = new FormData();
form.append('audio', createReadStream(wavPath));
form.append('task', 'transcribe');
form.append('webhook_url', webhookUrl);
if (language) form.append('language', language);
const res = await fetch(`${whisperUrl()}/jobs`, {
method: 'POST',
body: form,