deepfakes/faceswap · critical · FaceswapError

There is a mismatch between the number of frames found in th

Error message

There is a mismatch between the number of frames found in the video file ({len(pts_time)}) and the number of frames found in the alignments file ({len(self.data)}).\nThis can be caused by a number of issues:\n  - The video has a Variable Frame Rate and FFMPEG is having a hard time calculating the correct number of frames.\n  - You are working with a Merged Alignments file. This is not supported for your current use case.\nYou should either extract the video to individual frames, re-encode the video at a constant frame rate and re-run extraction or work with a dedicated alignments file for your requested video.

What it means

Raised by Alignments.save_timecodes/update flow (lib/align/alignments.py) after merging video frame timestamps (pts_time from FFProbe) into the alignments data: the number of keys in the alignments file must equal the number of frames FFProbe found in the video. A mismatch means the alignments were generated against a different frame set than the current video.

Source

Thrown at lib/align/alignments.py:198

        sample_filename = next(fname for fname in self.data)
        basename = sample_filename[:sample_filename.rfind("_")]
        ext = os.path.splitext(sample_filename)[-1]
        logger.debug("sample filename: '%s', base filename: '%s' extension: '%s'",
                     sample_filename, basename, ext)
        logger.info("Saving video meta information to Alignments file")

        for idx, pts in enumerate(pts_time):
            meta:  dict[T.Literal["pts_time", "keyframe"], int] = {"pts_time": pts,
                                                                   "keyframe": idx in keyframes}
            key = f"{basename}_{idx + 1:06d}{ext}"
            if key not in self.data:
                self.data[key] = AlignmentsEntry(video_meta=meta)
            else:
                self.data[key].video_meta = meta

        logger.debug("Alignments count: %s, timestamp count: %s", len(self.data), len(pts_time))
        if len(self.data) != len(pts_time):
            raise FaceswapError(
                "There is a mismatch between the number of frames found in the video file "
                f"({len(pts_time)}) and the number of frames found in the alignments file "
                f"({len(self.data)}).\nThis can be caused by a number of issues:"
                "\n  - The video has a Variable Frame Rate and FFMPEG is having a hard time "
                "calculating the correct number of frames."
                "\n  - You are working with a Merged Alignments file. This is not supported for "
                "your current use case."
                "\nYou should either extract the video to individual frames, re-encode the "
                "video at a constant frame rate and re-run extraction or work with a dedicated "
                "alignments file for your requested video.")
        self._io.save()

    # << VALIDATION >> #
    def frame_exists(self, frame_name: str) -> bool:
        """Check whether a given frame_name exists within the alignments :attr:`data`.

        Parameters
        ----------

View on GitHub (pinned to f530cb7508)

Solutions

  1. Extract the video to individual PNG frames and run the job against the frames folder instead of the video file
  2. Re-encode the video at a constant frame rate (e.g. ffmpeg -i in.mp4 -vsync cfr -r <fps> out.mp4) and re-run extraction
  3. Use a dedicated alignments file for this video rather than a merged alignments file (check with the alignments tool that frame counts match)

Example fix

# before: VFR video used directly
faceswap.py align -a alignments.fsa -v input.mov  # -> frame count mismatch

# after: re-encode CFR first
ffmpeg -i input.mov -vsync cfr -r 30 -pix_fmt yuv420p input_cfr.mp4
faceswap.py align -a alignments_cfr.fsa -v input_cfr.mp4
Defensive patterns

Strategy: validation

Validate before calling

import subprocess, json

def ffprobe_frame_count(video):
    out = subprocess.check_output([
        "ffprobe", "-v", "error", "-select_streams", "v:0",
        "-count_packets", "-show_entries", "stream=nb_read_packets",
        "-of", "json", video])
    return json.loads(out)["streams"][0]["nb_read_packets"]

# compare against len(alignments.frames) before running the video-meta job

Try / catch

try:
    alignments.save_timecodes(...)
except FaceswapError as err:
    if "mismatch between the number of frames" in str(err):
        # fall back to frames-based workflow
        extract_to_frames(video)
    else:
        raise

Prevention

When it happens

Trigger: Calling the alignments tool job that writes video metadata (e.g. 'extract'/'sort' video-meta update) on a Variable Frame Rate video where FFProbe miscounts frames, or supplying an alignments file that covers more than one video (merged alignments) or was built from extracted frames then reused against a re-encoded video.

Common situations: Processing a screen recording or phone-captured VFR video; using one combined alignments file across multiple clips; re-encoding a video (changing frame count) after extraction; ffmpeg/ffprobe version differences in frame counting.

Related errors


AI-assisted analysis of deepfakes/faceswap@f530cb7508 (2026-08-15). Data as JSON: /api/errors/0159eada119a10ce. Report an issue: GitHub.