diff --git a/apps/api/src/jobs/worker.ts b/apps/api/src/jobs/worker.ts index bc562c92..270a7156 100644 --- a/apps/api/src/jobs/worker.ts +++ b/apps/api/src/jobs/worker.ts @@ -369,7 +369,7 @@ async function processToolJob(job: Job): Promise { const finalError = isCanceled ? "Canceled" : isTimeout - ? `Timed out after ${Math.round(timeoutMs / 1000)}s` + ? `Timed out after ${Math.round(timeoutMs / 1000)}s. On the first run the background-removal model may still be downloading; the image may be too large for CPU inference; or the worker may be busy or unavailable. Retry once the model has downloaded, or try a smaller image.` : errorMessage; // Log genuine processing faults at error level (clients only ever see diff --git a/tests/integration/platform/worker-timeout.test.ts b/tests/integration/platform/worker-timeout.test.ts index d4c50ee2..c89415e0 100644 --- a/tests/integration/platform/worker-timeout.test.ts +++ b/tests/integration/platform/worker-timeout.test.ts @@ -116,6 +116,10 @@ describe("Worker timeout classification", () => { // Error message must mention timeout const error = finalRow?.error as { message: string }; expect(error.message).toMatch(/timed out after 1s/i); + // Fail-fast message must include actionable guidance (issue #494) + expect(error.message).toMatch( + /model may still be downloading|too large for CPU|worker may be busy/i, + ); // Both attempts ran (attempts column is set at the start of each attempt) expect(finalRow?.attempts).toBe(2);