decolua/9router · error
Runway polling timeout
Error message
Runway polling timeout
What it means
The Runway generation task did not reach SUCCEEDED (or FAILED/CANCELLED) within POLL_TIMEOUT_MS, so parseResponse aborts polling and throws. Runway image/video generation is asynchronous: the code sleeps POLL_INTERVAL_MS between status checks and gives up once the deadline passes, even though the task may still be running upstream.
Source
Thrown at open-sse/handlers/imageProviders/runwayml.js:42
if (isVideo) {
return { promptText: body.prompt, model, ratio, duration: 5, ...(body.image ? { promptImage: body.image } : {}) };
}
return { promptText: body.prompt, model, ratio, ...(body.image ? { referenceImages: [{ uri: body.image }] } : {}) };
},
async parseResponse(response, { headers }) {
const { id } = await response.json();
if (!id) throw new Error("Runway: no task id returned");
const taskUrl = `${BASE_URL}/tasks/${id}`;
const deadline = Date.now() + POLL_TIMEOUT_MS;
while (Date.now() < deadline) {
await sleep(POLL_INTERVAL_MS);
const r = await fetch(taskUrl, { headers });
if (!r.ok) throw new Error(`Runway status ${r.status}`);
const s = await r.json();
if (s.status === "SUCCEEDED") return s;
if (s.status === "FAILED" || s.status === "CANCELLED") throw new Error(s.failure || "Runway task failed");
}
throw new Error("Runway polling timeout");
},
normalize: (responseBody) => {
const outputs = Array.isArray(responseBody.output) ? responseBody.output : [];
return { created: nowSec(), data: outputs.map((url) => ({ url })) };
},
};
View on GitHub (pinned to 90b52e06ff)
Solutions
- Increase POLL_TIMEOUT_MS in open-sse/handlers/imageProviders/_base.js to cover the slowest model you use (video generation can take several minutes)
- Fetch `${BASE_URL}/tasks/{id}` manually with the Bearer token to check the task's current status — if still running, the timeout was simply too short
- Reduce POLL_INTERVAL_MS slightly to catch terminal transitions sooner (avoid hammering the API; keep a few seconds minimum)
- Resubmit the generation; if tasks routinely time out on one model, switch to a faster image model (gen4_image)
Example fix
// before (open-sse/handlers/imageProviders/_base.js) export const POLL_TIMEOUT_MS = 60_000; // after — allow up to 5 minutes for slow video tasks export const POLL_TIMEOUT_MS = 300_000;
Defensive patterns
Strategy: retry
Try / catch
try {
const result = await runwayImageCall(params);
} catch (err) {
if (err.message === "Runway polling timeout") {
// task may still be running upstream — poll /tasks/{id} manually with backoff
// before resubmitting, to avoid duplicate generations
} else {
throw err;
}
} Prevention
- Size POLL_TIMEOUT_MS to your slowest model (video tasks need minutes, not seconds)
- For video generations prefer longer timeouts; only image models fit tight windows
- On timeout, reconcile against the task endpoint instead of immediately resubmitting
When it happens
Trigger: parseResponse's `while (Date.now() < deadline)` loop expires — the task stays in RUNNING/QUEUED/PENDING for longer than POLL_TIMEOUT_MS while polling every POLL_INTERVAL_MS.
Common situations: Long video generations (image_to_video, 5s clips) or queues under heavy load exceeding the polling window; task stuck forever due to a Runway-side stall; POLL_TIMEOUT_MS configured too short for the chosen model; clock skew is irrelevant here but very slow networks add per-poll latency, effectively shrinking the window.
Understand the failure class
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- NanoBanana polling timeout
- Runway task failed
- BFL: no polling_url returned
- NanoBanana status ${r.status}
- NanoBanana generation failed
AI-assisted analysis of decolua/9router@90b52e06ff (2026-08-30).
Data as JSON: /api/errors/ff84ef119d728fa6.
Report an issue: GitHub.