{"record":{"id":"fb7bef1b7f5f4854","repo":"unslothai/unsloth","slug":"width-and-height-must-be-sent-together-or-both-om","errorCode":null,"errorMessage":"width and height must be sent together, or both omitted","messagePattern":"width and height must be sent together, or both omitted","errorType":"validation","errorClass":"ValueError","httpStatus":422,"severity":"error","filePath":"studio/backend/models/inference.py","lineNumber":3812,"sourceCode":"                \"keyframes and references cannot be combined: MiniMax-H3 runs them against \"\n                \"different denoiser partitions\"\n            )\n        return self\n\n    @model_validator(mode = \"after\")\n    def _keyframe_canvas_needs_both_axes(self) -> \"VideoGenerateRequest\":\n        # Omit both axes for \"match source\", or provide both for an explicit canvas.\n        # KEYFRAME requests only. There a half-specified canvas is silently discarded:\n        # _resolve_keyframes matches the source aspect whenever either axis is missing, so the\n        # axis that was sent never reaches the render and the API would accept one recipe and\n        # draw another. Without a keyframe the backend deliberately resolves the missing axis\n        # from the family's default preset -- validate_video_request_shape and generate() both\n        # document and implement that -- so applying the rule to every request would reject\n        # half-specified LTX, Wan, Hunyuan and prompt-only H3 calls that have always been valid.\n        if not (self.first_frame or self.last_frame):\n            return self\n        if (self.width is None) != (self.height is None):\n            raise ValueError(\"width and height must be sent together, or both omitted\")\n        return self\n\n\nclass GalleryVideo(BaseModel):\n    \"\"\"A persisted clip's full generation recipe (the JSON sidecar of the MP4).\"\"\"\n\n    id: str = Field(..., description = \"Stable id (the on-disk filename stem)\")\n    url: str = Field(..., description = \"Relative URL to fetch the MP4 bytes\")\n    prompt: str = Field(..., description = \"Prompt used\")\n    negative_prompt: Optional[str] = Field(None, description = \"Negative prompt, if any\")\n    width: int = Field(..., description = \"Frame width\")\n    height: int = Field(..., description = \"Frame height\")\n    num_frames: int = Field(..., description = \"Number of frames\")\n    fps: int = Field(..., description = \"Playback frame rate\")\n    duration_s: float = Field(..., description = \"Clip duration in seconds\")\n    steps: int = Field(..., description = \"Denoising steps\")\n    guidance: float = Field(..., description = \"Guidance scale\")\n    guidance_2: Optional[float] = Field(","sourceCodeStart":3794,"sourceCodeEnd":3830,"githubUrl":"https://github.com/unslothai/unsloth/blob/203007d19051dcd2ae33876786d117c99f6b0368/studio/backend/models/inference.py#L3794-L3830","documentation":"Raised by the _keyframe_canvas_needs_both_axes model_validator on VideoGenerateRequest. For KEYFRAME requests only (first_frame or last_frame set), the width/height canvas must be either fully specified or fully omitted, because the backend's _resolve_keyframes silently matches the source aspect ratio whenever either axis is missing — a half-specified canvas would be accepted but drawn differently than requested. The validator deliberately returns early for non-keyframe requests, since other model families (LTX, Wan, Hunyuan) and prompt-only H3 calls have always allowed a single axis to be resolved from family defaults.","triggerScenarios":"POST to video generation with first_frame or last_frame set and exactly one of width/height provided, e.g. {\"first_frame\": b64, \"width\": 1280} with height omitted, or {\"last_frame\": b64, \"height\": 720} with width omitted.","commonSituations":"A UI where width comes from one input and height from a possibly-unset second input; code that computes height from an aspect ratio and passes it only when the math succeeds; copying a non-keyframe request template (where one axis is fine) and adding a keyframe to it.","solutions":["Send both width and height together for keyframe requests (compute the missing axis from the keyframe image's aspect ratio).","Or send neither axis and let the backend match the source aspect ratio of the keyframe.","Check your request builder for conditionally-set axis fields and make them all-or-nothing when first_frame/last_frame is present."],"exampleFix":"# before\nreq = {\"prompt\": p, \"first_frame\": b64, \"width\": 1280}  # height missing\n# after (explicit canvas)\nreq = {\"prompt\": p, \"first_frame\": b64, \"width\": 1280, \"height\": 720}\n# after (match source aspect)\nreq = {\"prompt\": p, \"first_frame\": b64}","handlingStrategy":"validation","validationCode":"def canvas_pairwise(req: dict) -> bool:\n    if not (req.get(\"first_frame\") or req.get(\"last_frame\")):\n        return True\n    return (req.get(\"width\") is None) == (req.get(\"height\") is None)","typeGuard":"function canvasOk(req: { first_frame?: unknown; last_frame?: unknown; width?: number | null; height?: number | null }): boolean {\n  if (!(req.first_frame || req.last_frame)) return true;\n  return (req.width == null) === (req.height == null);\n}","tryCatchPattern":null,"preventionTips":["For keyframe requests compute the missing axis from the keyframe image aspect ratio before submit","Treat width/height as one optional pair in request types (one object or null), not two independent optional fields"],"tags":["pydantic","validation","video-generation","keyframes","aspect-ratio"],"backgroundTag":null,"analyzedSha":"203007d19051dcd2ae33876786d117c99f6b0368","analyzedAt":"2026-08-15T02:48:39.846Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}