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.