Video Generation API Errors: Rejected Submissions, Failed Tasks and Moderation, Explained (2026)
A video generation API fails in two places: at submission, or later when the task ends as FAILED. This guide maps each error cocodot's video API returns to its cause and fix, with a polling script that retries only what is worth retrying.
1. First question: rejected at submission, or failed as a task?
cocodot's video API is asynchronous. You POST to https://cocodot.co/api/ai/video/generations and receive a task ID, then poll GET https://cocodot.co/api/ai/video/generations/{id}. The status is PENDING while the video is being generated, SUCCEEDED with a videoUrl when it is done, or FAILED with an error field. That gives errors two exits. A rejected submission comes back at once as an HTTP status with a message; parameter problems are caught before any task is created. A failed task is one that was accepted and given an ID, then ended as FAILED, with the reason in error. Working out which exit you are looking at is the whole first step of debugging: a 400 at submission is fixed by changing the request, while a FAILED task needs you to read the error text before deciding whether to change the content or simply try again later. Log the task ID, the full request body, the HTTP status and the raw message together. If you need to contact support, the task ID is the first thing to give them. One practical note for teams outside China: the messages are returned in Chinese, so branch your code on the HTTP status and on the field names the messages mention, and show the raw text to a human when in doubt.
2. 400 unsupported fields: each model family has its own schema
The three video model families use different request formats, and cocodot accepts a fixed set of fields per family. Anything extra returns a 400 whose message lists the fields the model does accept. Under the current API, the Seedance models accept model, content, resolution and duration; Kling accepts model, content, prompt, mode, duration and sound; Hailuo accepts model, content, prompt, resolution and duration, and Hailuo 2.3 Fast additionally accepts first_frame_image. The classic mistake is taking a working request for one family, changing only the model, and sending it to another: resolution sent to Kling, or prompt sent to Seedance. The fix is mechanical: remove whatever the message does not list. A wrong model name returns a separate unsupported-video-model error; the valid IDs are the id values returned by GET https://cocodot.co/api/ai/video/models.
// Seedance
{"model": "seedance-2.5", "content": [{"type": "text", "text": "A harbour at dawn, slow push-in"}], "resolution": "720p", "duration": 5}
// Kling
{"model": "kling-v3", "prompt": "Rainy street at night, neon reflections", "mode": "std", "duration": 5, "sound": "off"}
// Hailuo 2.3 Fast (image to video)
{"model": "hailuo-2.3-fast", "prompt": "Slow push-in", "first_frame_image": "https://…/frame.png", "resolution": "768P", "duration": 6}3. Duration, resolution and mode: only listed combinations are open
cocodot only opens parameter combinations whose pricing has been checked tier by tier. Anything outside that set is rejected at submission, with no task created and nothing charged. The messages fall into a few patterns. One says a model currently only opens certain resolutions, so the one you sent is outside the range. Another says it only opens certain durations in seconds. A third, for Kling, says only the std or pro mode is supported. The last says the combination is not open: each value is individually valid, but together they are not in the open price table. Two more relate to the duration format. Duration must be a positive whole number of seconds; the number 5 and the string "5" both work, while 5.5 or "5s" do not. Kling is billed per second, so leaving duration out returns an error asking you to specify it. The low-effort fix is to call GET https://cocodot.co/api/ai/video/models before you change parameters, read each model's allowed values (resolutions, durations, modes) and validate in your own code, so invalid requests never leave your machine.
4. Hailuo 2.3 Fast first frames, and images in content
On cocodot, Kling and Hailuo only accept text in content. Putting an image or a video there returns a 400 that also tells you where the Hailuo 2.3 Fast first frame belongs: the first_frame_image field. Hailuo 2.3 Fast is image-to-video, so that field is required for it. It takes either a publicly reachable http(s) image URL or a base64 string starting with data:image/, and the value must be 10MB or smaller. A URL containing spaces, a non-http(s) scheme, or base64 without the data:image/ prefix is rejected with the generic message that the submission failed and that you should check resolution, duration and image or video references. Debug it in this order: open the image URL in a private browser window to confirm it loads without a login; check that your code has not appended whitespace or a newline to it; for base64, check the prefix and the size. Integration details for both Hailuo models are in /hub/hailuo-2-3-api.
5. Seedance: --rs and --dur in the prompt are rejected
Some Seedance guides suggest appending text commands such as --rs 1080p --dur 12 to the prompt to set resolution and duration. On cocodot, a Seedance prompt containing these flags is rejected with a 400 telling you to set resolution and duration through the resolution and duration fields, and that other parameters are not open. The specification has to be in fields so the estimated charge can be calculated from them before submission. The fix: move resolution into the resolution field and duration into the duration field, and keep only the scene description in the prompt. The block is not limited to --rs and --dur; flags such as --ratio, --fps, --seed and --watermark are rejected too. If your prompts were exported in bulk from another tool, strip any segment that starts with two hyphens and a letter before submitting. Available specs and billing for the Seedance versions are covered in /hub/seedance-2-5-api.
6. Moderation failures: change the content, do not resend it
The safety-review message can show up in two places: as a 400 at submission, or as the error on a task that was accepted and then ended as FAILED during generation. Neither is charged, and both mean the prompt or the input media (first frame, reference video) did not pass review this time. Sending the same content again does not change what is being reviewed, and it ties up one of your concurrent task slots while it runs. Work through it this way: look for descriptions involving real people, violence or sensitive settings in the prompt and rephrase them more neutrally; then check the first frame and any reference media themselves; if the prompt looks clean but the task was stopped mid-generation, adjust the camera and action description and try again. The review criteria themselves are outside the scope of this guide, and we make no promise about which content will pass.
7. 429, 503, insufficient balance, the concurrency cap, 401 and 403
429 (video requests too frequent, not charged) and 503 (video service temporarily unavailable, not charged) are the errors worth retrying: wait a few seconds, double the wait each time, and set a maximum number of attempts. Insufficient wallet balance comes with the estimated charge for this video in US dollars and means the estimate is more than your available balance; tasks still generating have already had their own estimates held. Video cannot use the $0.5 trial credit, so top up first; top-ups are available via Alipay / WeChat Pay (RMB), USDT / USDC. At most 3 videos generating at the same time means you have hit the per-account limit on tasks in progress; wait for earlier tasks to finish or fail, and queue batch jobs on your side. 401 means the key is missing or invalid (the message reads missing api key or invalid api key); check the Authorization: Bearer cocodot-… header. 403 means an account-level requirement is not met, commonly identity verification (L1); that message includes the link to complete it, in both Chinese and English.
8. A polling and retry pattern that does not waste money
Failed video tasks are not charged, but a resubmission that succeeds is a new task, billed normally. A script that blindly resends a failing request ten times wastes time at best; at worst several attempts succeed while nobody is watching. The pattern below branches on the error. Any 400, 401 or 403 is never retried; the raw message is raised for a person to fix. 429 and 503 back off with a cap. A FAILED task is regenerated at most once, and the error text is printed so a human can tell a moderation failure from a generic one before trying again. Polling every fifteen seconds or so is plenty. Once you see SUCCEEDED, download the video and store it yourself promptly, because the link expires. The useful part here is specific to this API: the retry decision is driven by what the API actually returns for each case, not by a blanket rule on status codes.
import time, requests
BASE = "https://cocodot.co/api/ai/video/generations"
H = {"Authorization": "Bearer cocodot-..."}
def submit(body, tries=4):
for i in range(tries):
r = requests.post(BASE, json=body, headers=H, timeout=90)
if r.ok:
return r.json()["id"]
if r.status_code in (429, 503):
time.sleep(5 * 2 ** i)
continue
raise RuntimeError(f"{r.status_code} {r.text}") # fix the request; do not retry
raise RuntimeError("repeated 429/503; try again later")
def run(body, max_regen=1):
for attempt in range(max_regen + 1):
tid = submit(body)
while True:
time.sleep(15)
t = requests.get(f"{BASE}/{tid}", headers=H, timeout=30).json()
if t["status"] == "SUCCEEDED":
return t["videoUrl"] # download and store it promptly
if t["status"] == "FAILED":
break
err = t.get("error") or ""
print("task", tid, "failed:", err)
if err != "视频生成失败,本次未扣费,请稍后重试。":
raise RuntimeError(err) # review unknown or moderation errors
raise RuntimeError("generation keeps failing: review parameters and content")Common errors from cocodot's video API (messages are returned in Chinese; the English below is a summary)
| Error (summary) | Where it appears | Cause | What to do |
|---|---|---|---|
| Request contains unsupported fields; allowed fields: … | Submit, 400 | A field this model family does not accept | Trim the body to the fields the message lists |
| Only … is open / this parameter combination is not open | Submit, 400 | Resolution, duration or mode outside the open range | Read allowed from GET /api/ai/video/models and adjust |
| This model does not accept images or video in content | Submit, 400 | Kling and Hailuo content is text only | Send the Hailuo 2.3 Fast first frame in first_frame_image |
| Prompt text flags are not supported | Submit, 400 | Seedance prompt contains --rs, --dur and similar | Remove them; use the resolution and duration fields |
| Video task submission failed, not charged | Submit, 400 | A parameter or image/video reference was not accepted | Check resolution, duration and the image URL or base64 |
| Content did not pass the safety review, not charged | Submit 400, or task FAILED | Prompt or input media did not pass review | Change the content; do not resend it unchanged |
| Too many requests / service temporarily unavailable | Submit, 429 / 503 | Requests too dense, or the service is temporarily unavailable | Back off and retry with a cap |
| Video generation failed, not charged, try again later | Task FAILED | Generation failed; no finer reason given | Retry once or twice later; if it keeps failing, review parameters and content |