{"record":{"id":"5234b8e251715099","repo":"invoke-ai/InvokeAI","slug":"cross-filesystem-image-move-is-not-supported-sou","errorCode":null,"errorMessage":"Cross-filesystem image move is not supported: {source} -> {destination}","messagePattern":"Cross-filesystem image move is not supported: (.+?) -> (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"invokeai/app/services/image_moves/image_moves_default.py","lineNumber":876,"sourceCode":"                self._fsync_dir(path.parent)\n        self.image_files.evict_cache_paths([old_path, new_path, old_thumbnail_path, new_thumbnail_path])\n        self.mark_item_moved(job_id, image_name)\n\n    def _remove_empty_parents(self, start: Path, root: Path) -> None:\n        root = root.resolve()\n        current = start.resolve()\n        while current != root and current.is_relative_to(root):\n            try:\n                current.rmdir()\n            except OSError:\n                return\n            current = current.parent\n\n    def _assert_same_filesystem(self, source: Path, destination: Path) -> None:\n        source_parent = source.parent\n        destination_parent = self._nearest_existing_parent(destination.parent)\n        if source_parent.stat().st_dev != destination_parent.stat().st_dev:\n            raise ValueError(f\"Cross-filesystem image move is not supported: {source} -> {destination}\")\n\n    def _nearest_existing_parent(self, path: Path) -> Path:\n        current = path\n        while not current.exists():\n            if current.parent == current:\n                raise FileNotFoundError(f\"No existing parent found for {path}\")\n            current = current.parent\n        return current\n\n    def _fsync_file(self, path: Path) -> None:\n        try:\n            with path.open(\"rb\") as file:\n                os.fsync(file.fileno())\n        except OSError as e:\n            self._logger.debug(\"Unable to fsync file: %s: %s\", path, e)\n\n    def _fsync_dir(self, path: Path) -> None:\n        try:","sourceCodeStart":858,"sourceCodeEnd":894,"githubUrl":"https://github.com/invoke-ai/InvokeAI/blob/0b6a024f2ff6a86bfb953dcdb9cc504ef7397a06/invokeai/app/services/image_moves/image_moves_default.py#L858-L894","documentation":"_assert_same_filesystem, called during preflight_moves, compares st_dev of the source directory and the nearest existing ancestor of the destination directory. If they differ, the move would cross filesystem boundaries (os.rename would fail with EXDEV), so the service raises ValueError('Cross-filesystem image move is not supported').","triggerScenarios":"Running preflight_moves where the images root and the destination subfolder root live on different mounts — e.g. destination on a NFS/network volume, separate SSD/HDD, tmpfs, or Docker bind mount not sharing a filesystem with the source.","commonSituations":"Docker container with images dir and output dir on different mounts; NAS mounts (NFS/SMB); symlinking an output folder to another disk; WSL/Windows drive boundaries (/mnt/c vs ~).","solutions":["Move the destination onto the same filesystem/mount as the source images directory (or vice versa).","If using Docker/bind mounts, mount the destination path from the same volume as the images root.","Replace a cross-device symlink with a same-filesystem directory or bind-mount the second disk into the tree so st_dev matches.","Apply the subfolder strategy within the existing single filesystem instead of redirecting output to another device."],"exampleFix":"// before\n# source: /srv/invokeai/images (dev 8:1)\n# destination: /mnt/nas/invokeai/images (dev 0:xx) -> ValueError\n// after: keep both on one filesystem\nln -s /mnt/nas/invokeai /srv/invokeai/nas  # NO - still cross-device for moves\n# instead mount nas under the same fs tree or move outputs dir onto /srv/invokeai","handlingStrategy":"validation","validationCode":"import os\nfrom pathlib import Path\ndef same_filesystem(src: Path, dst: Path) -> bool:\n    p = dst\n    while not p.exists():\n        p = p.parent\n    return os.stat(src.parent).st_dev == os.stat(p).st_dev","typeGuard":"def paths_share_device(a: Path, b: Path) -> bool:\n    return a.stat().st_dev == b.stat().st_dev","tryCatchPattern":"try:\n    service.preflight_moves(job_id)\nexcept ValueError as e:\n    if \"Cross-filesystem image move\" in str(e):\n        # re-point destination onto the images filesystem or copy instead of move","preventionTips":["Keep the output/destination directory on the same mount as the images root","In Docker, mount both paths from the same volume","Avoid symlinked output folders on other devices","Run preflight_moves and check st_dev early in setup scripts"],"tags":["filesystem","image-move","mount","configuration"],"backgroundTag":"cross-device-move-unsupported","analyzedSha":"0b6a024f2ff6a86bfb953dcdb9cc504ef7397a06","analyzedAt":"2026-08-29T04:46:49.967Z","schemaVersion":2},"datasetVersion":"2026-08-29T07:17:48.351Z"}