decolua/9router · error
Runway status ${r.status}
Error message
Runway status ${r.status} What it means
Thrown by Runway's parseResponse when a GET to /tasks/{id} during polling returns a non-2xx status (runwayml.js:37). As with [50], the poll HTTP call itself failed — task creation succeeded but the status-check request did not, and the first bad response aborts generation with no retry.
Source
Thrown at open-sse/handlers/imageProviders/runwayml.js:37
};
},
buildBody: (model, body) => {
const isVideo = !model.includes("image");
const ratio = sizeToAspectRatio(body.size);
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
- Check the reported status: 401/403 → refresh the Runway API key and ensure the X-Runway-Version header matches a supported version.
- 429 → lower polling frequency (increase POLL_INTERVAL_MS) or reduce concurrent Runway requests, then retry.
- 404 → confirm BASE_URL is the correct Runway API host and the task id path `${BASE_URL}/tasks/${id}` is well-formed.
- 5xx → wait and re-submit; check Runway's status page if it persists.
- Harden parseResponse to retry transient 429/5xx poll responses a few times before giving up.
Example fix
// before
const r = await fetch(taskUrl, { headers });
if (!r.ok) throw new Error(`Runway status ${r.status}`);
// after
const r = await fetch(taskUrl, { headers });
if (!r.ok) {
if ((r.status === 429 || r.status >= 500) && ++pollErrors <= 3) continue;
throw new Error(`Runway status ${r.status}`);
}
pollErrors = 0; Defensive patterns
Strategy: retry
Validate before calling
// Before submitting: check the task-status endpoint is reachable and auth works
const probe = await fetch(`${BASE_URL}/tasks`, { headers: { Authorization: `Bearer ${key}`, "X-Runway-Version": "2024-11-06" } });
if (probe.status === 401 || probe.status === 403) throw new Error("Runway key invalid or lacking task-read permission");
if (!BASE_URL?.startsWith("https://")) throw new Error(`Suspicious Runway BASE_URL: ${BASE_URL}`); Type guard
function isTransientPollStatus(status) { return status === 429 || status >= 500; } Try / catch
try {
const task = await imageProvider.parseResponse(response, { headers });
} catch (e) {
const m = e.message.match(/^Runway status (\d+)$/);
if (m) {
const status = Number(m[1]);
if (status === 401 || status === 403) await refreshRunwayKey();
if (isTransientPollStatus(status)) return retryWithBackoff(); // resubmit; poll errors are not persisted state
}
throw e;
} Prevention
- Increase POLL_INTERVAL_MS and cap concurrent Runway tasks to stay under status-poll rate limits.
- Refresh long-lived API keys before lengthy video generations.
- Keep the X-Runway-Version header current; a rejected version surfaces as 4xx on task polls.
- Alert on Runway status >= 500 patterns to catch provider incidents early.
- Confirm BASE_URL correctness — a wrong host makes every /tasks/{id} poll 404.
When it happens
Trigger: `fetch(taskUrl, { headers })` in the polling loop at runwayml.js:36-37 returns non-ok: 401/403 (key revoked or insufficient permissions mid-run), 404 (task id not found or deleted), 429 (Runway rate limits task-status polling), 5xx (Runway outage), or BASE_URL misconfigured so /tasks/{id} 404s on the wrong host.
Common situations: Long video generations (duration: 5s+) where the API key rotates/expires during the poll window; polling too aggressively for Runway's rate limits under parallel requests; Runway returning transient 5xx during incidents; X-Runway-Version header rejected for newer task endpoints; BASE_URL typo making every /tasks/{id} request 404.
Related errors
- NanoBanana status ${r.status}
- Runway: no task id returned
- MiMo bootstrap failed: ${response.status}
- BFL: no polling_url returned
- NanoBanana generation failed
AI-assisted analysis of decolua/9router@90b52e06ff (2026-08-30).
Data as JSON: /api/errors/61fece12df7da825.
Report an issue: GitHub.