{"record":{"id":"fb6630b4f2239c42","repo":"nexu-io/open-design","slug":"openrouter-video-submit-submitresp-status-tr","errorCode":null,"errorMessage":"openrouter video submit ${submitResp.status}: ${truncate(submitText, 240)}","messagePattern":"openrouter video submit (.+?): (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"apps/daemon/src/media/index.ts","lineNumber":2029,"sourceCode":"    ];\n  }\n\n  // ── Step 1: Submit the generation request ──────────────────────────\n  const submitResp = await fetch(`${baseUrl}/videos`, withMediaRequestInit(ctx, {\n    method: 'POST',\n    headers: {\n      'authorization': `Bearer ${credentials.apiKey}`,\n      'content-type': 'application/json',\n      // OpenRouter attribution headers per\n      // https://openrouter.ai/docs/app-attribution\n      'HTTP-Referer': 'https://opendesign.dev',\n      'X-Title': 'Open Design',\n    },\n    body: JSON.stringify(body),\n  }));\n  const submitText = await submitResp.text();\n  if (!submitResp.ok) {\n    throw new Error(\n      `openrouter video submit ${submitResp.status}: ${truncate(submitText, 240)}`,\n    );\n  }\n  let submitData: any;\n  try {\n    submitData = JSON.parse(submitText);\n  } catch {\n    throw new Error(`openrouter video non-JSON: ${truncate(submitText, 200)}`);\n  }\n\n  const jobId = submitData?.id;\n  const pollingUrl = submitData?.polling_url;\n  if (!jobId || !pollingUrl) {\n    throw new Error(\n      `openrouter video submit returned no job id or polling_url: ${truncate(submitText, 200)}`,\n    );\n  }\n","sourceCodeStart":2011,"sourceCodeEnd":2047,"githubUrl":"https://github.com/nexu-io/open-design/blob/5be4028344c2eb4c667c5a97bda8f750c5597ef7/apps/daemon/src/media/index.ts#L2011-L2047","documentation":"Thrown by renderOpenRouterVideo when the POST to submit an async video generation job (with frame_images for i2v when ctx.imageRef is set) returns non-2xx. The error embeds submitResp.status and the first 240 chars of the body. Unlike the image chat-completion path, video uses a dedicated async submit endpoint and returns a job id plus polling_url on success.","triggerScenarios":"401/403 (bad key), 402 (no credits — video models are typically premium), 404 (model slug not enabled for video on the key), 413/422 (frame_images base64 too large or malformed for i2v), 429 (rate limit), or upstream 5xx from the underlying video provider.","commonSituations":"Account has no credits for the premium video model, ctx.imageRef base64 exceeds the provider size cap, model id not opted into video, or a provider-side (Seedance/Kling/etc.) incident.","solutions":["Read status+body: 402→add credits, 404→fix model slug, 413→reduce image size, 429→slow down, 5xx→retry/backoff.","Confirm wireModel is a video-capable OpenRouter slug and that the account is enabled for it.","For i2v, verify ctx.imageRef resolved to a valid base64 data URL within the provider's size limits.","Retry once on 5xx; video backends are flaky under load."],"exampleFix":"// before\nif (!submitResp.ok) {\n  throw new Error(`openrouter video submit ${submitResp.status}: ${truncate(submitText, 240)}`);\n}\n\n// after — status hints\nif (!submitResp.ok) {\n  const hint = submitResp.status === 402 ? ' (no credits)'\n    : submitResp.status === 413 ? ' (image too large)'\n    : submitResp.status === 404 ? ' (unknown video model)'\n    : submitResp.status === 429 ? ' (rate limited)'\n    : '';\n  throw new Error(`openrouter video submit ${submitResp.status}${hint}: ${truncate(submitText, 240)}`);\n}","handlingStrategy":"try-catch","validationCode":"function describeOpenRouterVideoSubmitError(status: number, body: string): string {\n  const hint = status === 401 ? 'bad key'\n    : status === 402 ? 'no credits for premium video model'\n    : status === 404 ? 'unknown video model slug'\n    : status === 413 ? 'frame_images payload too large for i2v'\n    : status === 429 ? 'rate limited'\n    : status >= 500 ? 'upstream video provider outage — retry'\n    : 'see body';\n  return `openrouter video submit ${status} (${hint}): ${truncate(body, 240)}`;\n}\n\nfunction isRetryableSubmitStatus(status: number): boolean {\n  return status === 429 || status >= 500;\n}","typeGuard":"function isOpenRouterVideoModelSlug(model: string): boolean {\n  return /seedance|kling|wan|sora|veo|cogvideo|hailuo|ltx-video|minimax/i.test(model);\n}","tryCatchPattern":"for (let attempt = 0; attempt < 2; attempt++) {\n  const r = await submitOpenRouterVideo(ctx, credentials);\n  if (r.ok) { /* parse and poll */ }\n  if (attempt === 0 && isRetryableSubmitStatus(r.status)) { await sleep(2000); continue; }\n  throw new Error(describeOpenRouterVideoSubmitError(r.status, await r.text()));\n}","preventionTips":["Verify the account has credits; video models are premium and 402 is common on free keys.","Keep ctx.imageRef base64 within the provider's i2v size cap to avoid 413.","Confirm the slug is video-capable in the OpenRouter catalogue.","Retry on 5xx/429 with backoff before surfacing."],"tags":["openrouter","video-generation","http-status","credentials","provider-api"],"backgroundTag":null,"analyzedSha":"5be4028344c2eb4c667c5a97bda8f750c5597ef7","analyzedAt":"2026-08-12T12:03:58.812Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}