D2: Fix upload retry 500 on missing file — async fire-and-forget
retryJob() now wraps the file read in a fire-and-forget async IIFE using fs/promises.readFile instead of readFileSync. When the original upload file has been cleaned up (common after failure), the IIFE catches the ENOENT error and marks the job as 'failed' with a 'retry failed:' prefix asynchronously — the endpoint returns 200 immediately. Includes 4 new integration tests: - Missing file returns immediately (no throw), job async-fails - ENOENT message propagated in job.error - 404 on unknown job still works (unchanged contract) - File present resets job to pending (runJob triggered)
This commit is contained in:
@@ -50,7 +50,11 @@ export async function startUploadJob(
|
||||
return job.id;
|
||||
}
|
||||
|
||||
/** Retry a failed/cancelled job — YouTube or upload-source. */
|
||||
/** Retry a failed/cancelled job — YouTube or upload-source.
|
||||
*
|
||||
* Upload retry reads the original file asynchronously (fire-and-forget IIFE)
|
||||
* so a missing file produces an async job failure instead of a synchronous
|
||||
* 500 crash. The outer function returns immediately. */
|
||||
export async function retryJob(jobId: string): Promise<void> {
|
||||
const job = getJob(jobId);
|
||||
if (!job) throw new Error('Job not found');
|
||||
@@ -58,12 +62,22 @@ export async function retryJob(jobId: string): Promise<void> {
|
||||
|
||||
const isUpload = !job.source.startsWith('http');
|
||||
if (isUpload) {
|
||||
const { readFileSync } = await import('fs');
|
||||
const uploadPath = getUploadPath(jobId, job.source);
|
||||
const buffer = readFileSync(uploadPath);
|
||||
runJob(jobId, { type: 'upload', buffer, filename: job.source }, job.audioMode as AudioMode).catch((err) => {
|
||||
console.error(`[pipeline] retry upload job ${jobId} failed:`, err);
|
||||
});
|
||||
// Fire-and-forget: missing file -> async job failure, not 500
|
||||
(async () => {
|
||||
try {
|
||||
const { readFile } = await import('fs/promises');
|
||||
const uploadPath = getUploadPath(jobId, job.source);
|
||||
const buffer = await readFile(uploadPath);
|
||||
runJob(jobId, { type: 'upload', buffer, filename: job.source }, job.audioMode as AudioMode).catch((err) => {
|
||||
console.error(`[pipeline] retry upload job ${jobId} failed:`, err);
|
||||
});
|
||||
} catch (err: unknown) {
|
||||
const message = err instanceof Error ? err.message : String(err);
|
||||
console.error(`[pipeline] retry upload job ${jobId} failed to read file:`, message);
|
||||
updateJob({ id: jobId, status: 'failed', error: `retry failed: ${message}` });
|
||||
emitProgress(jobId, { type: 'error', message: `retry failed: ${message}` });
|
||||
}
|
||||
})();
|
||||
} else {
|
||||
runJob(jobId, { type: 'youtube', url: job.source }, job.audioMode as AudioMode).catch((err) => {
|
||||
console.error(`[pipeline] retry job ${jobId} failed:`, err);
|
||||
|
||||
Reference in New Issue
Block a user