When you submit an async job to the HiAPI task API, a successful submission returns HTTP 200 with a taskId. The job then runs in the background, and when you poll it, it can end in a terminal fail state. The poll response looks like this — note the HTTP call itself is still 200:
{
"code": 200,
"data": {
"taskId": "tk-hiapi-01M4RK...",
"model": "seedream-5.0-lite/image-to-image",
"status": "fail",
"error": { "code": "TASK_FAILED", "message": "task failed" },
"created": 1791827316,
"completed": 1791827352,
"storage": "temp"
},
"message": "success"
}
This is what people mean by a "TASK_FAILED error": the request was accepted, the task ran, and it finished unsuccessfully. It is not an HTTP 4xx error and not a timeout. This guide shows how to read that payload and fix each cause.
First: TASK_FAILED is not an HTTP status
The single most important thing to understand:
- The HTTP status of the poll call is
200. The envelope even says"message": "success"— that only means the API call succeeded, not that your job did. "status": "fail"(insidedata) is how you know the job failed. For a finished job,data.statusis either"success"or"fail"."TASK_FAILED"is the value ofdata.error.code, not the status. Some failures carry a more specific code and a more useful message (see below).
So always poll GET /v1/tasks/{taskId} and branch on data.status. Never decide success from the HTTP code alone.
Common causes of a terminal fail
Ordered roughly by how often they show up.
1. A broken or non-public input image/video URL
This is the most common cause for image-to-image and image/video-to-video jobs. If your input references a URL the platform cannot fetch, the task is accepted but fails while running. A real terminal payload:
"error": {
"code": "INVALID_REQUEST",
"message": "The image URL could not be downloaded. Please provide a reachable public HTTP(S) URL and try again."
}
Here the message is explicit. Other models surface the same root cause as the generic TASK_FAILED / "task failed".
Fix: host the input on a public HTTPS URL and verify it before submitting:
curl -I "https://your-host.example.com/input.png"
# must return 200 and a direct image/video content-type
# not 401/403/404, and not an HTML page
Expired pre-signed URLs, localhost/private addresses, and links that require a login are the usual culprits.
2. A transient or upstream generation failure
Sometimes a well-formed job fails with the generic error.code: "TASK_FAILED", message: "task failed" and no further detail. These are usually transient.
Fix: retry the job once or twice. If it keeps failing with identical input, simplify the input (shorter prompt, drop optional fields) and re-run to isolate the trigger.
3. An option the model rejects
Most invalid parameters are caught at submission time as an HTTP 400 (you never get a taskId) — see the next section. Occasionally a specific combination of otherwise-valid options is only rejected once the job runs, landing in data.status: "fail". The error.message names the offending option.
Fix: read data.error.message, then check the model's supported inputs on its model page (for example, the GPT Image 2 model page) and remove or correct the field it names.
How to tell TASK_FAILED apart from 4xx and timeouts
Three different situations all get called "it failed." They have different fixes:
| What you see | What it means | Where to fix |
|---|---|---|
POST returns non-2xx (400/401/402/429) and no taskId | The request was rejected; no task ever ran | Fix the request / key / balance (links below) |
POST returns 200 + taskId, poll shows data.status: "fail" | TASK_FAILED — the job ran and failed | Read data.error.message (this guide) |
POST returns 200 + taskId, poll keeps returning a non-terminal status (e.g. handling) past your deadline | Timeout, not a failure | Keep polling / raise your timeout / retry |
If you never received a taskId, you did not hit a TASK_FAILED — you hit a request error:
HTTP 400invalid, missing, or unsupported parameter → Fix HiAPI 400 Bad RequestHTTP 401invalid API key → Invalid API Key Errors- Insufficient balance (rejected at creation, so there is no task to fail) → Fix HiAPI 402 Insufficient Balance
- Unknown or unlisted model id (
MODEL_UNAVAILABLE) → "model not found" / "model not available" - A task that never reaches a terminal state → HiAPI Task Timeout and 504 Errors
Fix checklist
- Poll the task, don't trust the POST. Submission only tells you the job was queued. Call
GET /v1/tasks/{taskId}and look atdata.status. - Confirm it's a real failure:
data.status == "fail"(the HTTP code will be200). - Read
data.error.codeanddata.error.message. The message is your primary signal. - If it mentions the input URL: make the URL public and reachable; verify with
curl -I(200 + a direct media content-type). - If the message is generic (
"task failed"): treat it as transient — retry; if it persists, simplify the input. - Confirm the model id is currently listed via
GET /v1/modelsand matches exactly what you sent. An unknown id fails at submit as400 MODEL_UNAVAILABLE, not as a terminal fail. - Check the model's supported fields on its model page and drop anything unsupported (those usually 400 at submit).
- No
taskIdat all? It wasn't a TASK_FAILED — jump to the matching request-error guide above.
Minimal verification example
Before debugging a specific job, confirm your key and connectivity with one clean request to the unified task endpoint. A bare model id works (no endpoint suffix required):
# 1) Submit — expect HTTP 200 with a taskId
curl -s https://api.hiapi.ai/v1/tasks \
-H "Authorization: Bearer sk-YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"model":"gpt-image-2","input":{"prompt":"a red bicycle on a white background","aspect_ratio":"1:1"}}'
# -> {"code":200,"data":{"taskId":"tk-hiapi-..."},"message":"success"}
# 2) Poll — swap in the taskId you got back
curl -s https://api.hiapi.ai/v1/tasks/tk-hiapi-xxxxxxxx \
-H "Authorization: Bearer sk-YOUR_KEY"
# -> data.status is "handling" while it runs, then "success" or "fail"
If step 1 returns 200 and a taskId, your key and endpoint are fine and any failure is a per-job issue, not a setup issue. If step 1 returns 401, your key is wrong — manage keys in your HiAPI dashboard. (This example submits a real generation.)
FAQ
Is TASK_FAILED an HTTP status code?
No. The HTTP call returns 200. TASK_FAILED is the value of data.error.code inside a terminal payload whose data.status is "fail".
data.status says "fail" but I was looking for "TASK_FAILED" — which is correct?
Both are correct, in their place. data.status is always "success" or "fail" for a finished job. "TASK_FAILED" is a generic data.error.code; a failed job may instead carry a more specific code (such as INVALID_REQUEST) with an actionable message.
My task is stuck on handling and never fails or succeeds.
That's a timeout, not a failure. Keep polling or raise your client timeout; see HiAPI Task Timeout and 504 Errors.
I got a 400, 401, or 402 instead of a taskId.
Those are request rejections — no task was created, so nothing "failed." Fix the request, key, or balance using the guides linked above.
Was I charged for a failed task? See Are failed HiAPI requests charged? for how up-front reservations and refunds work on failures.
How do I know it's a broken input URL?
Read data.error.message. A URL problem typically says the image or video could not be downloaded; verify the link with curl -I returns 200 and a direct media file.



