{"record":{"id":"2ae41dcc9ce76dac","repo":"decolua/9router","slug":"runway-no-task-id-returned","errorCode":null,"errorMessage":"Runway: no task id returned","messagePattern":"Runway: no task id returned","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"open-sse/handlers/imageProviders/runwayml.js","lineNumber":31,"sourceCode":"  buildHeaders: (creds) => {\n    const key = creds?.apiKey || creds?.accessToken;\n    return {\n      \"Content-Type\": \"application/json\",\n      \"Authorization\": `Bearer ${key}`,\n      \"X-Runway-Version\": \"2024-11-06\",\n    };\n  },\n  buildBody: (model, body) => {\n    const isVideo = !model.includes(\"image\");\n    const ratio = sizeToAspectRatio(body.size);\n    if (isVideo) {\n      return { promptText: body.prompt, model, ratio, duration: 5, ...(body.image ? { promptImage: body.image } : {}) };\n    }\n    return { promptText: body.prompt, model, ratio, ...(body.image ? { referenceImages: [{ uri: body.image }] } : {}) };\n  },\n  async parseResponse(response, { headers }) {\n    const { id } = await response.json();\n    if (!id) throw new Error(\"Runway: no task id returned\");\n    const taskUrl = `${BASE_URL}/tasks/${id}`;\n    const deadline = Date.now() + POLL_TIMEOUT_MS;\n    while (Date.now() < deadline) {\n      await sleep(POLL_INTERVAL_MS);\n      const r = await fetch(taskUrl, { headers });\n      if (!r.ok) throw new Error(`Runway status ${r.status}`);\n      const s = await r.json();\n      if (s.status === \"SUCCEEDED\") return s;\n      if (s.status === \"FAILED\" || s.status === \"CANCELLED\") throw new Error(s.failure || \"Runway task failed\");\n    }\n    throw new Error(\"Runway polling timeout\");\n  },\n  normalize: (responseBody) => {\n    const outputs = Array.isArray(responseBody.output) ? responseBody.output : [];\n    return { created: nowSec(), data: outputs.map((url) => ({ url })) };\n  },\n};\n","sourceCodeStart":13,"sourceCodeEnd":49,"githubUrl":"https://github.com/decolua/9router/blob/90b52e06ffd666b7929554211474d01588f6b1f8/open-sse/handlers/imageProviders/runwayml.js#L13-L49","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nconst { id } = await response.json();\nif (!id) throw new Error(\"Runway: no task id returned\");\n// after\nconst submit = await response.json();\nif (!response.ok || submit.error) throw new Error(`Runway submit failed: ${JSON.stringify(submit).slice(0, 300)}`);\nconst { id } = submit;\nif (!id) throw new Error(`Runway: no task id returned in ${JSON.stringify(submit).slice(0, 300)}`);","handlingStrategy":"validation","validationCode":"// After the submit call, before polling\nconst submit = await response.json();\nif (!submit || typeof submit !== \"object\") throw new Error(\"Runway submit returned non-object body\");\nif (submit.error || submit.message) throw new Error(`Runway submit rejected: ${JSON.stringify(submit).slice(0, 300)}`);\nif (typeof submit.id !== \"string\" || !submit.id) throw new Error(\"Runway submit response missing task id\");","typeGuard":"function hasRunwayTaskId(body) {\n  return !!body && typeof body === \"object\" && typeof body.id === \"string\" && body.id.length > 0;\n}","tryCatchPattern":"try {\n  const task = await imageProvider.parseResponse(response, { headers });\n} catch (e) {\n  if (e.message === \"Runway: no task id returned\") {\n    console.error(\"Runway submit envelope:\", lastSubmitBody); // captured raw submit JSON\n    // Validate creds + quota, then retry once\n    return retryAfterCredentialCheck();\n  }\n  throw e;\n}","preventionTips":["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."],"tags":["missing-field","async-polling","image-generation","api-key","schema-change"],"backgroundTag":"missing-task-id-in-response","analyzedSha":"90b52e06ffd666b7929554211474d01588f6b1f8","analyzedAt":"2026-08-30T21:05:45.952Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}