unslothai/unsloth · error · ValueError

keyframes and references cannot be combined: MiniMax-H3 runs

Error message

keyframes and references cannot be combined: MiniMax-H3 runs them against different denoiser partitions

What it means

Raised by the same model_validator on VideoGenerateRequest (MiniMax-H3). MiniMax-H3 runs first_frame/last_frame keyframes and free-form reference media (reference_images/videos/audios) against different denoiser partitions, so the two conditioning modes are mutually exclusive in one request. The check fires when any reference list is non-empty AND either first_frame or last_frame is set. It is the third check in the validator, after the budget and standalone-audio checks.

Source

Thrown at studio/backend/models/inference.py:3793

                if len(item) > 32 * 1024 * 1024:
                    raise ValueError("each reference must be at most 32 MiB (base64)")
        return value

    @model_validator(mode = "after")
    def _references_fit_the_models_budget(self) -> "VideoGenerateRequest":
        images = self.reference_images or []
        videos = self.reference_videos or []
        audios = self.reference_audios or []
        total = len(images) + len(videos) + len(audios)
        if total > 12:
            raise ValueError(f"MiniMax-H3 takes at most 12 references in total, got {total}")
        # Standalone audio must accompany an image or video reference.
        if audios and not images and not videos:
            raise ValueError(
                "reference audio needs at least one reference image or video to go with"
            )
        if (images or videos or audios) and (self.first_frame or self.last_frame):
            raise ValueError(
                "keyframes and references cannot be combined: MiniMax-H3 runs them against "
                "different denoiser partitions"
            )
        return self

    @model_validator(mode = "after")
    def _keyframe_canvas_needs_both_axes(self) -> "VideoGenerateRequest":
        # Omit both axes for "match source", or provide both for an explicit canvas.
        # KEYFRAME requests only. There a half-specified canvas is silently discarded:
        # _resolve_keyframes matches the source aspect whenever either axis is missing, so the
        # axis that was sent never reaches the render and the API would accept one recipe and
        # draw another. Without a keyframe the backend deliberately resolves the missing axis
        # from the family's default preset -- validate_video_request_shape and generate() both
        # document and implement that -- so applying the rule to every request would reject
        # half-specified LTX, Wan, Hunyuan and prompt-only H3 calls that have always been valid.
        if not (self.first_frame or self.last_frame):
            return self
        if (self.width is None) != (self.height is None):

View on GitHub (pinned to 203007d190)

Solutions

  1. Decide the conditioning mode: either keep first_frame/last_frame and drop all reference_* fields, or keep references and drop the keyframes.
  2. If you need both a start anchor and extra references, fold the anchor image into reference_images and remove first_frame.
  3. Audit request-building code for stale keyframe fields left over from a previous model's template.

Example fix

# before
req = {
  "prompt": p,
  "first_frame": start_b64,
  "reference_images": [style_b64],
}
# after (mode 1: keyframes only)
req = {"prompt": p, "first_frame": start_b64}
# after (mode 2: references only)
req = {"prompt": p, "reference_images": [start_b64, style_b64]}
Defensive patterns

Strategy: validation

Validate before calling

def keyframes_xor_references(req: dict) -> bool:
    has_refs = any(req.get(k) for k in ("reference_images", "reference_videos", "reference_audios"))
    has_kf = bool(req.get("first_frame") or req.get("last_frame"))
    return not (has_refs and has_kf)

Type guard

function conditioningExclusive(req: Record<string, unknown>): boolean {
  const refs = ['reference_images','reference_videos','reference_audios'].some(k => Array.isArray(req[k]) && (req[k] as unknown[]).length > 0);
  const kf = Boolean(req.first_frame || req.last_frame);
  return !(refs && kf);
}

Try / catch

try { await client.generate(req) } catch (e) { if (/cannot be combined/.test(String(e))) { delete req.first_frame; delete req.last_frame; return client.generate(req); } throw e; }

Prevention

When it happens

Trigger: POST to the video generation endpoint with e.g. {"first_frame": "...", "reference_images": ["..."]}, or {"last_frame": "...", "reference_audios": ["..."], "reference_videos": ["..."]} — any non-empty reference list combined with either keyframe field.

Common situations: Migrating from another model family (LTX, Wan, Hunyuan) that permits mixing keyframes with references; a UI that shows keyframe and reference widgets simultaneously and submits both; incrementally adding reference images to an existing keyframe workflow without clearing first_frame/last_frame.

Related errors


AI-assisted analysis of unslothai/unsloth@203007d190 (2026-08-15). Data as JSON: /api/errors/ec52d3b629f8c943. Report an issue: GitHub.