huggingface/transformers · error · AssertionError

Provided path ({save_directory}) should be a directory, not

Error message

Provided path ({save_directory}) should be a directory, not a file

What it means

FeatureExtractorBase.save_pretrained expects a directory to write preprocessor_config.json into. If save_directory points at an existing file, writing would clobber it, so os.path.isfile triggers an immediate AssertionError before makedirs.

Source

Thrown at src/transformers/feature_extraction_utils.py:399

        return cls.from_dict(feature_extractor_dict, **kwargs)

    def save_pretrained(self, save_directory: str | os.PathLike, push_to_hub: bool = False, **kwargs):
        """
        Save a feature_extractor object to the directory `save_directory`, so that it can be re-loaded using the
        [`~feature_extraction_utils.FeatureExtractionMixin.from_pretrained`] class method.

        Args:
            save_directory (`str` or `os.PathLike`):
                Directory where the feature extractor JSON file will be saved (will be created if it does not exist).
            push_to_hub (`bool`, *optional*, defaults to `False`):
                Whether or not to push your model to the Hugging Face model hub after saving it. You can specify the
                repository you want to push to with `repo_id` (will default to the name of `save_directory` in your
                namespace).
            kwargs (`dict[str, Any]`, *optional*):
                Additional key word arguments passed along to the [`~utils.PushToHubMixin.push_to_hub`] method.
        """
        if os.path.isfile(save_directory):
            raise AssertionError(f"Provided path ({save_directory}) should be a directory, not a file")

        os.makedirs(save_directory, exist_ok=True)

        if push_to_hub:
            commit_message = kwargs.pop("commit_message", None)
            repo_id = kwargs.pop("repo_id", save_directory.split(os.path.sep)[-1])
            repo_id = hf_api().create_repo(repo_id, exist_ok=True, **kwargs).repo_id
            files_timestamps = self._get_files_timestamps(save_directory)

        # If we have a custom config, we copy the file defining it in the folder and set the attributes so it can be
        # loaded from the Hub.
        if self._auto_class is not None:
            custom_object_save(self, save_directory, config=self)

        # If we save using the predefined names, we can load using `from_pretrained`
        output_feature_extractor_file = os.path.join(save_directory, FEATURE_EXTRACTOR_NAME)

        self.to_json_file(output_feature_extractor_file)

View on GitHub (pinned to a597f97485)

Solutions

  1. Pass the directory: fe.save_pretrained('./my-extractor') — the JSON filename is chosen automatically
  2. If the path exists as a file by mistake, remove/rename it first
  3. To control the filename, save into a directory and rename the emitted preprocessor_config.json afterwards

Example fix

# before
fe.save_pretrained("checkpoints/preprocessor_config.json")

# after
fe.save_pretrained("checkpoints")
Defensive patterns

Strategy: validation

Validate before calling

import os

def ensure_dir(path):
    if os.path.isfile(path):
        raise ValueError(f"{path} is a file; pass its parent directory")
    os.makedirs(path, exist_ok=True)
    return path

Type guard

def is_directory_path(path) -> bool:
    import os
    return not os.path.isfile(path)

Try / catch

try:
    fe.save_pretrained(path)
except AssertionError as e:
    if "should be a directory" in str(e):
        import os
        fe.save_pretrained(os.path.dirname(path))
    else:
        raise

Prevention

When it happens

Trigger: Calling fe.save_pretrained(path) where path is a file — commonly passing the intended JSON filename itself (e.g. 'config.json') instead of its parent folder, or a path created by an earlier file write.

Common situations: Confusion between 'save to this file' vs 'save into this directory' APIs; scripts that create the target path as a file first; typos where the filename is used as the directory.

Related errors


AI-assisted analysis of huggingface/transformers@a597f97485 (2026-08-14). Data as JSON: /api/errors/52d9eb733fdcc2dd. Report an issue: GitHub.