invoke-ai/InvokeAI · error · ValueError

Video {video_name} is {width}x{height}; H.264 encoding requi

Error message

Video {video_name} is {width}x{height}; H.264 encoding requires even dimensions. Re-encode or crop the source to even width and height first.

What it means

Thrown by _validate_even_dimensions in the frame-range extraction/export path. Output is encoded with libx264 + yuv420p and macro_block_size=1 to preserve exact source dimensions, which requires even width and height. Odd dimensions would either fail or be silently rescaled by the encoder.

Source

Thrown at invokeai/app/invocations/video_frame_extract_range.py:50

from invokeai.app.invocations.primitives import VideoOutput
from invokeai.app.services.session_processor.session_processor_common import CanceledException
from invokeai.app.services.shared.invocation_context import InvocationContext
from invokeai.app.util.video_encoding import make_mp4_writer
from invokeai.app.util.video_thumbnails import decoder_frame_count, iter_video_frames, probe_video


class _FrameWriter(Protocol):
    def append_data(self, frame: np.ndarray) -> None: ...


def _validate_even_dimensions(width: int, height: int, video_name: str) -> None:
    """Raises if the source dimensions can't be encoded as-is with libx264 + yuv420p.

    We encode with ``macro_block_size=1`` to preserve the source dimensions exactly
    (imageio's default of 16 silently rescales), which requires even width and height.
    """
    if width % 2 or height % 2:
        raise ValueError(
            f"Video {video_name} is {width}x{height}; H.264 encoding requires even dimensions. "
            "Re-encode or crop the source to even width and height first."
        )


def _write_frame_range(
    frames: Iterator[np.ndarray],
    writer: _FrameWriter,
    start: int,
    end: int,
    is_canceled: Optional[Callable[[], bool]] = None,
) -> int:
    """Streams frames[start..end] (inclusive) from a lazy decoder into the writer.

    Frames are appended one at a time as they stream past — the full range is never
    materialized in RAM. The upload cap admits files whose decoded frames would run to
    tens of gigabytes, so peak memory here must stay one-frame-sized regardless of the
    requested range. Decoding stops as soon as ``end`` has been written. Returns the

View on GitHub (pinned to 0b6a024f2f)

Solutions

  1. Crop or scale the source so width and height are even (e.g. scale=trunc(iw/2)*2:trunc(ih/2)*2)
  2. Use ffmpeg to re-encode with even dimensions before extraction
  3. Round crop boxes down to even values in your pipeline

Example fix

// before
ffmpeg -i in.mp4 -vf crop=853:480 out.mp4  # odd width 853
// after
ffmpeg -i in.mp4 -vf "crop=852:480" out.mp4  # even dimensions
Defensive patterns

Strategy: validation

Validate before calling

width, height, _, _ = probe_video(path)
if width % 2 or height % 2:
    raise ValueError(f'{path} has odd dimensions {width}x{height}; fix before range extract')

Type guard

def even_dims(width: int, height: int) -> bool:
    return width % 2 == 0 and height % 2 == 0

Try / catch

try:
    out = range_extract.invoke(context)
except ValueError as e:
    if 'even dimensions' in str(e):
        run_ffmpeg(['-i', path, '-vf', 'crop=trunc(iw/2)*2:trunc(ih/2)*2', fixed_path])
        out = rebuild(video=fixed_path).invoke(context)
    else:
        raise

Prevention

When it happens

Trigger: invoke() -> _validate_even_dimensions(width, height) where width % 2 != 0 or height % 2 != 0, from probe_video of the source video.

Common situations: Sources cropped or scaled to odd sizes (e.g. 853x480); older footage with odd dimensions; programmatic crops computed without parity rounding.

Related errors


AI-assisted analysis of invoke-ai/InvokeAI@0b6a024f2f (2026-08-29). Data as JSON: /api/errors/34d8f21a730f76e5. Report an issue: GitHub.