Validation rules¶
Every message validate can emit, what causes it, and what to do about it.
Errors, warnings and notes¶
The three levels are not severity labels. They answer three different questions.
| Level | Question it answers | Affects exit code |
|---|---|---|
ERROR |
Will the API reject this request? | Yes — exit 1 |
WARNING |
Will the API accept this request and do something other than what you meant? | No |
note |
What will this request actually produce and cost? | No |
The distinction matters most for warnings. A request with generate_audio: false is a
perfectly valid request; it will be accepted, it will render, and you will be billed. It
just will not do the thing the flag says. Failing the build over that would be wrong —
the request works. Staying silent would also be wrong, because somebody wrote that flag
expecting an effect. So it warns, and the exit code stays 0.
If your pipeline should treat warnings as fatal, gate on the output text rather than patching the tool; see Running it in CI.
Duration¶
`duration` is missing¶
There is no default. The field is required.
`duration` must be a whole number of seconds, got 7.5¶
The check is isinstance(duration, int), so 7.5 fails and so does "8". Note that
Python treats True as an int, so "duration": true passes this check and then fails
the range check as the value 1 — an edge case worth knowing if your request documents
are assembled by templating.
`duration` must be 4-15, got 3¶
The supported window is 4 to 15 seconds inclusive. Many write-ups state a five-second
floor; the API reference accepts four, and h3_spec.json records the discrepancy in
duration.commonError.
The frame-grid note¶
note duration 10s renders as 243 frames = 10.125s (n=14).
The only whole second in range is 8.000s (192 frames).
This is the rule that surprises people, so it is worth setting out in full.
H3 renders in frame blocks. The frame count follows frames = 17n + 5, and output is
24 fps. Your requested duration is snapped to the nearest grid point, so what you get is
(17n + 5) / 24 seconds for whichever n is closest.
For a requested duration to land on a whole second, you need:
(17n + 5) mod 24 = 0
17n ≡ 19 (mod 24)
17 is its own inverse modulo 24 — 17 × 17 = 289 = 12 × 24 + 1 — so multiplying both
sides by 17 gives n ≡ 17 × 19 ≡ 323 ≡ 11 (mod 24). Within the 4–15 second window n
runs from 6 to 20, and the only solution is n = 11: 192 frames, exactly 8.000
seconds.
Anyone can check that with a calculator, which is why it is odd that it is not documented anywhere upstream. Two practical consequences:
- Cutting to music, or stitching clips end to end? Use 8 seconds. Every other value in range drifts, and the drift accumulates across a sequence.
- Budgeting? You are billed on rendered seconds. A 10-second request bills as 10.125.
The snapping function searches a wider range than the supported window (grid_rows(0, 30))
and breaks ties downward, matching the behaviour of the ComfyUI nodes.
Resolution¶
`resolution` is missing¶
Required, like duration.
`resolution` must be one of ['768P', '2K'], got '1080p' - this model has no 1080p and no 4K path¶
The trailing hint appears only when the value you passed is one of 1080p, 1080, 4k
or 2160p, case-insensitively. It exists because that specific mistake has a specific
cause: several highly ranked pages about this model list resolutions it does not have,
and people copy them.
There is nothing between 768P and 2K, and nothing above 2K. A request for 1080p is rejected, not downgraded to the nearest tier.
The 2K path is also not an upscale. The 768P result is fed back through the model together with your original context and generated again, which is why small on-screen text survives it and why 2K costs more per second rather than a flat surcharge.
Aspect ratio¶
`aspect_ratio` must be one of ['21:9', '16:9', '4:3', '1:1', '3:4', '9:16'], got '5:4'¶
Checked only when the key is present. Six ratios, no arbitrary dimensions.
Worth knowing, though h3check cannot check it for you: in image-to-video modes the
output ratio follows the input image, not this field. If you need a 9:16 clip from a
16:9 still, crop the still. Setting aspect_ratio will not do it.
Prompt¶
`prompt` is 7412 characters, limit is 7000¶
A character count, not a token count. The ceiling is generous enough that hitting it usually means something is being concatenated in a loop.
The prompt itself is expected in three fields — subject, action and camera, sound — with
(S1) and (S2) speaker tags for dialogue. h3check does not enforce that structure,
because a prompt that ignores it is still a legal request; it just tends to produce worse
video. If you want the structure enforced as you write, the
prompt generator at minimax-h3ai.video
prints the skeleton and checks it against a dozen rules.
Reference inputs¶
11 images exceeds the cap of 9¶
Per-type caps: nine images, three video clips, three audio files. The message is emitted once per type.
13 reference files exceeds the total cap of 12 - the per-type caps add up to 15, but the total is 12¶
This is the headline rule. Nine plus three plus three is fifteen, fifteen is the number several write-ups print, and the API caps the total at twelve. Every individual field can be at its documented maximum and the request still fails.
The failure mode this prevents is not a rejected request — it is a rejected request after you have spent the time uploading up to 64 MB of assets.
A full breakdown of the reference caps, including per-file size limits, accepted containers and codecs, pixel bounds and aspect-ratio bounds, is set out in this write-up of H3 reference-to-video.
reference video totals 21s, budget is 15s¶
Separate budgets for video and audio, 15 seconds each. Only entries that are JSON objects
with a numeric seconds key are counted; bare strings contribute to the count rules but
not to this one.
reference video clip of 18s is outside 2-15s¶
Each individual clip must be between 2 and 15 seconds. Same bounds for audio.
WARNING ref2va with no reference files silently degrades to text-to-video¶
"mode": "ref2va" with an empty or absent references block is accepted, billed, and
rendered as if you had sent a plain text prompt. Nothing in the response tells you the
references were missing. This is the single most expensive silent failure in the API,
because it looks exactly like a successful render.
Audio¶
WARNING audio cannot be switched off¶
Picture and 32 kHz stereo audio come out of the same forward pass. There is no dub step,
no separate audio model, no on/off switch, and no separate line on the bill — because
there is no separate step to bill for. Code that sets generate_audio: false is not
being rejected; it is being ignored.
If you need a silent file, strip the audio track after the fact.
Cost¶
note hosted cost at 2K: $1.3163 for 10.125 rendered seconds¶
Emitted whenever both duration and resolution are usable. The rate comes from
hostedPricing.perOutputSecond in the spec file and is applied to the rendered
duration, not the requested one.