deepfakes/faceswap · error · FaceswapError

Landmark based masks cannot be created for {self._landmark_t

Error message

Landmark based masks cannot be created for {self._landmark_type.name}

What it means

Raised by MaskAlignmentsFile._get_slices in lib/align/aligned_mask.py when the mask's landmark_type has no entry in the lookup dict for the requested area. For areas 'eye'/'mouth' the lookup is LANDMARK_PARTS (contains only LM_2D_68, LM_2D_98, LM_2D_4); for 'face'/'face_extended' it is LANDMARK_MASK_PARTS (contains only LM_2D_68 and LM_2D_98). Faceswap throws this because it has no slice definitions to build a landmark-based mask for that landmark type/area combination.

Source

Thrown at lib/align/aligned_mask.py:510

    def __repr__(self) -> str:
        """Pretty print for logging"""
        params = {f"{k[1:]}": format_array(v) if isinstance(v, np.ndarray) else v
                  for k, v in self.__dict__.items()
                  if k in ("_area", "_landmark_type", "_landmarks", "_size",
                           "_dilation", "_blur_kernel", "_blur_type", "blur_passes")}
        s_params = ", ".join(f"{k}={repr(v)}" for k, v in params.items())
        return f"{self.__class__.__name__}({s_params})"

    def _get_slices(self) -> list[slice] | list[list[slice]]:
        """Obtain the slices that will extract the points for the given area and landmark type

        Returns
        -------
        The slices required to extract landmark points for creating a mask
        """
        parts = LANDMARK_PARTS if self._area in ("eye", "mouth") else LANDMARK_MASK_PARTS
        if self._landmark_type not in parts:
            raise FaceswapError(
                f"Landmark based masks cannot be created for {self._landmark_type.name}")

        lm_parts = parts[self._landmark_type]
        mapped = {"mouth": ["mouth_outer"],
                  "eye": ["right_eye", "left_eye"],
                  "face": list(lm_parts),
                  "face_extended": list(lm_parts)}[self._area]

        if not all(parts in lm_parts for parts in mapped):
            raise FaceswapError(
                f"Landmark based masks cannot be created for {self._landmark_type.name}")

        if self._area in ("eye", "mouth"):
            retval: list[slice] | list[list[slice]] = [slice(*lm_parts[v][:2]) for v in mapped]
        else:
            retval = [[slice(*p) for p in T.cast(list[tuple[int, int]], lm_parts[v])]
                      for v in mapped]
        logger.trace("[LM_MASK] area: '%s', slices: %s",  # type:ignore[attr-defined]

View on GitHub (pinned to f530cb7508)

Solutions

  1. Re-extract or re-detect faces with a detector/detector+2D-68 landmark pipeline (e.g. use the 68-point or 98-point landmark flavor) so landmark_type is LM_2D_68 or LM_2D_98
  2. For eye/mouth masks, ensure alignments contain at least the eye/mouth landmark indices; switch to a different mask type (e.g. components, extended, dfl) which does not rely on LANDMARK_MASK_PARTS
  3. Check the alignments file's landmark type with the alignments tool (e.g. 'alignments tool > spatial' or inspect DetectedFace.landmarks) to confirm which LandmarkType was stored

Example fix

# before
mask = MaskAlignmentsFile(area="face",
                          landmark_type=LandmarkType.LM_2D_4,
                          landmarks=face.landmarks,  # only 4 points
                          size=128)

# after
# LM_2D_4 has no face-mask slices; use a supported type
mask = MaskAlignmentsFile(area="face",
                          landmark_type=LandmarkType.LM_2D_68,
                          landmarks=face.landmarks_68,
                          size=128)
Defensive patterns

Strategy: validation

Validate before calling

from lib.align.constants import LandmarkType, LANDMARK_PARTS, LANDMARK_MASK_PARTS

def can_build_mask(area, lmk_type):
    parts = LANDMARK_PARTS if area in ("eye", "mouth") else LANDMARK_MASK_PARTS
    return lmk_type in parts

Type guard

from lib.align.constants import LandmarkType, LANDMARK_PARTS, LANDMARK_MASK_PARTS

def maskable_landmark_type(area: str, lmk: LandmarkType) -> bool:
    """True if MaskAlignmentsFile supports (area, lmk) combination."""
    table = LANDMARK_PARTS if area in ("eye", "mouth") else LANDMARK_MASK_PARTS
    return lmk in table

Try / catch

try:
    mask = MaskAlignmentsFile(area=area, landmark_type=lmk_type, landmarks=lms, size=128)
except FaceswapError:
    logger.warning("mask unsupported for %s/%s, skipping", area, lmk_type.name)
    mask = None

Prevention

When it happens

Trigger: Constructing MaskAlignmentsFile(area=..., landmark_type=..., landmarks=...) where area is 'face' or 'face_extended' and landmark_type is LM_2D_4, LM_2D_51 or LM_3D_26; or area is 'eye'/'mouth' with landmark_type LM_2D_51 or LM_3D_26. Happens at __init__ time (self.mask = self.generate_mask()).

Common situations: Running the mask plugin 'landmarks' with a detector that outputs 4-point landmarks (e.g. a 68-point pipeline replaced by a 4-point detector) then requesting a face mask; loading old alignments converted to LM_2D_51; requesting extended/face landmark masks after extraction with a minimal landmark set.

Related errors


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