Lightning-AI/pytorch-lightning · error · ValueError

`ModelCheckpoint(save_last='link')` is only supported for lo

Error message

`ModelCheckpoint(save_last='link')` is only supported for local file paths, got `dirpath={dirpath}`.

What it means

ModelCheckpoint's `save_last='link'` creates a filesystem symlink ('last.ckpt' -> best checkpoint), which only works on local paths. In `setup()`, if `save_last == 'link'` and the dirpath scheme isn't a local file protocol (e.g. s3://, gs://), a ValueError is raised.

Source

Thrown at src/lightning/pytorch/callbacks/model_checkpoint.py:331

    def state_key(self) -> str:
        return self._generate_state_key(
            monitor=self.monitor,
            mode=self.mode,
            every_n_train_steps=self._every_n_train_steps,
            every_n_epochs=self._every_n_epochs,
            train_time_interval=self._train_time_interval,
        )

    @override
    def setup(self, trainer: "pl.Trainer", pl_module: "pl.LightningModule", stage: str) -> None:
        dirpath = self.__resolve_ckpt_dir(trainer)
        dirpath = trainer.strategy.broadcast(dirpath)
        self.dirpath = dirpath
        self._fs = get_filesystem(self.dirpath or "")
        if trainer.is_global_zero and stage == "fit":
            self.__warn_if_dir_not_empty(self.dirpath)
        if self.save_last == "link" and not _is_local_file_protocol(self.dirpath):
            raise ValueError(
                f"`ModelCheckpoint(save_last='link')` is only supported for local file paths, got `dirpath={dirpath}`."
            )

    @override
    def on_train_start(self, trainer: "pl.Trainer", pl_module: "pl.LightningModule") -> None:
        self._last_time_checked = time.monotonic()

    @override
    def on_train_batch_end(
        self,
        trainer: "pl.Trainer",
        pl_module: "pl.LightningModule",
        outputs: STEP_OUTPUT,
        batch: Any,
        batch_idx: int,
    ) -> None:
        """Save checkpoint on train batch end if we meet the criteria for `every_n_train_steps`"""
        # For manual optimization, we need to handle saving differently

View on GitHub (pinned to 9fed5c27d2)

Solutions

  1. Use `save_last=True` instead of 'link' for remote paths (copies rather than symlinks)
  2. Or keep dirpath local (e.g. './checkpoints') and sync to remote separately
  3. Or use 'link' only where the filesystem supports symlinks

Example fix

# before
ModelCheckpoint(dirpath='s3://my-bucket/run1', save_last='link')
# after
ModelCheckpoint(dirpath='s3://my-bucket/run1', save_last=True)
Defensive patterns

Strategy: validation

Validate before calling

from urllib.parse import urlparse
scheme = urlparse(dirpath or '').scheme
if save_last == 'link' and scheme not in ('', 'file'):
    save_last = True  # fall back to copy for remote storage

Type guard

def link_save_last_ok(dirpath: str) -> bool:
    from urllib.parse import urlparse
    return urlparse(dirpath or '').scheme in ('', 'file')

Prevention

When it happens

Trigger: `ModelCheckpoint(dirpath='s3://bucket/ckpt', save_last='link')` or letting default dirpath resolve to a remote fsspec URL, then starting a fit. Only the 'link' mode is restricted; `save_last=True` (copy) works remotely.

Common situations: Cloud storage checkpoints on S3/GCS/Azure; Lightning's default remote checkpointing in cluster setups; switching a working local config to a remote dirpath without changing save_last.

Related errors


AI-assisted analysis of Lightning-AI/pytorch-lightning@9fed5c27d2 (2026-08-28). Data as JSON: /api/errors/c1d167695ebee4a0. Report an issue: GitHub.