decolua/9router · error
Runway: no task id returned
Error message
Runway: no task id returned
What it means
Thrown by Runway's parseResponse when the submit response JSON contains no `id` field at runwayml.js:31. Runway's async flow requires the submit response to carry a task id that is then polled at /tasks/{id}; without it the pipeline cannot proceed, so it throws immediately after submission.
Source
Thrown at open-sse/handlers/imageProviders/runwayml.js:31
buildHeaders: (creds) => {
const key = creds?.apiKey || creds?.accessToken;
return {
"Content-Type": "application/json",
"Authorization": `Bearer ${key}`,
"X-Runway-Version": "2024-11-06",
};
},
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
- Log the raw submit response body to see what actually came back instead of {id: ...}.
- Verify the Runway API key/accessToken is valid and has credits/quota — re-authenticate or top up and retry.
- Check PROVIDER_MEDIA['runwayml'].imageConfig.baseUrl points to https://api.dev.runwayml.com (or the current documented host).
- Confirm the X-Runway-Version header ('2024-11-06') is still a supported API version; bump it if Runway deprecated it.
- If a proxy/gateway is in front, bypass it — it may be swallowing the response envelope that contains id.
Example fix
// before
const { id } = await response.json();
if (!id) throw new Error("Runway: no task id returned");
// after
const submit = await response.json();
if (!response.ok || submit.error) throw new Error(`Runway submit failed: ${JSON.stringify(submit).slice(0, 300)}`);
const { id } = submit;
if (!id) throw new Error(`Runway: no task id returned in ${JSON.stringify(submit).slice(0, 300)}`); Defensive patterns
Strategy: validation
Validate before calling
// After the submit call, before polling
const submit = await response.json();
if (!submit || typeof submit !== "object") throw new Error("Runway submit returned non-object body");
if (submit.error || submit.message) throw new Error(`Runway submit rejected: ${JSON.stringify(submit).slice(0, 300)}`);
if (typeof submit.id !== "string" || !submit.id) throw new Error("Runway submit response missing task id"); Type guard
function hasRunwayTaskId(body) {
return !!body && typeof body === "object" && typeof body.id === "string" && body.id.length > 0;
} Try / catch
try {
const task = await imageProvider.parseResponse(response, { headers });
} catch (e) {
if (e.message === "Runway: no task id returned") {
console.error("Runway submit envelope:", lastSubmitBody); // captured raw submit JSON
// Validate creds + quota, then retry once
return retryAfterCredentialCheck();
}
throw e;
} Prevention
- Verify Runway API key validity and remaining credits before submitting tasks.
- Pin and periodically review the X-Runway-Version header against Runway's changelog.
- Keep PROVIDER_MEDIA['runwayml'].imageConfig.baseUrl pointed at the documented API host.
- Always log the raw submit response when id is absent — the provider's error envelope explains why.
When it happens
Trigger: The POST to `${BASE_URL}/text_to_image` or `${BASE_URL}/image_to_video` returns 2xx but the parsed body lacks `id` — e.g. a 200-wrapped error envelope ({error: ...}), an auth/frontend that returns 200 with {success:false}, a proxy intercepting the call, or the account having no quota so Runway accepts but does not create a task.
Common situations: Expired or invalid Runway API key behind a gateway that still returns 200; wrong BASE_URL (imageConfig.baseUrl for PROVIDER_MEDIA['runwayml']) hitting a non-Runway endpoint that answers 200 with a different schema; Runway API version drift changing the submit response shape; org out of credits returning an error body with 200/202.
Related errors
- NanoBanana status ${r.status}
- NanoBanana polling timeout
- Runway status ${r.status}
- BFL: no polling_url returned
- NanoBanana generation failed
AI-assisted analysis of decolua/9router@90b52e06ff (2026-08-30).
Data as JSON: /api/errors/2ae41dcc9ce76dac.
Report an issue: GitHub.