goharbor/harbor · error

Path exists and the type is regular file

Error message

Path exists and the type is regular file

What it means

Thrown by prepare_dir() in make/photon/prepare/utils/misc.py when the directory it is asked to create (e.g. <data_volume>/registry, /etc/registry config dirs inside the prepare container) already exists but is a regular file (absolute_path.is_file()). Rather than overwrite or fail halfway, prepare refuses and aborts, protecting whatever content the file holds.

Source

Thrown at make/photon/prepare/utils/misc.py:68

        if storage_provider_config == "":
            raise Exception(
                "Error: no provider configurations are provided for provider %s" % storage_provider_name)

def validate_crt_subj(dirty_subj):
    subj_list = [item for item in dirty_subj.strip().split("/") \
        if len(item.split("=")) == 2 and len(item.split("=")[1]) > 0]
    return "/" + "/".join(subj_list)


def generate_random_string(length):
    return ''.join(secrets.choice(string.ascii_letters + string.digits) for _ in range(length))


def prepare_dir(root: str, *args, **kwargs) -> str:
    gid, uid = kwargs.get('gid'), kwargs.get('uid')
    absolute_path = Path(os.path.join(root, *args))
    if absolute_path.is_file():
        raise Exception('Path exists and the type is regular file')
    mode = kwargs.get('mode') or 0o755

    # we need make sure this dir has the right permission
    if not absolute_path.exists():
        absolute_path.mkdir(mode=mode, parents=True)
    elif not check_permission(absolute_path, mode=mode):
         absolute_path.chmod(mode)

    # if uid or gid not None, then change the ownership of this dir
    if not(gid is None and uid is None):
        dir_uid, dir_gid = absolute_path.stat().st_uid, absolute_path.stat().st_gid
        if uid is None:
            uid = dir_uid
        if gid is None:
            gid = dir_gid
        # We decide to recursively chown only if the dir is not owned by correct user
        # to save time if the dir is extremely large
        if not check_permission(absolute_path, uid, gid):

View on GitHub (pinned to 7b2fd08cc5)

Solutions

  1. Identify the offending file: the prepare log names the path being prepared when it aborts - inspect it (file <path>, cat) for valuable content
  2. Move it aside or delete it: mv <path> <path>.bak
  3. Ensure the parent data_volume is a real directory and mounts are directory mounts
  4. Re-run ./install.sh - prepare_dir will now mkdir and set ownership/permissions

Example fix

# on the Harbor host (before)
$ ls -l /data/registry
-rw-r--r-- 1 root root 0 Jan  1 00:00 /data/registry   # a file!

# after
$ mv /data/registry /data/registry.bak
$ ls -ld /data/registry
mkdir happens on next ./install.sh
Defensive patterns

Strategy: type-guard

Validate before calling

from pathlib import Path
# before calling prepare_dir(root, *args):
target = Path(root).joinpath(*args)
if target.exists() and target.is_file():
    raise SystemExit('%s exists and is a regular file - move it before prepare' % target)

Type guard

def is_clear_dir_path(p: str) -> bool:
    """True when p is a directory or does not exist (safe for prepare_dir)."""
    path = Path(p)
    return not path.exists() or path.is_dir()

Prevention

When it happens

Trigger: A regular file occupies the exact target path before install/prepare runs - e.g. someone created /data/registry as a file, a bind mount mounted a single file where a directory is expected, or leftover artifacts from previous experiments sit in the data_volume layout.

Common situations: Reusing a data_volume that previously held unrelated files; docker bind mounts created from single-file host paths; operators 'reserving' paths with touch; restoring partial backups where directories became files.

Related errors


AI-assisted analysis of goharbor/harbor@7b2fd08cc5 (2026-08-16). Data as JSON: /api/errors/991054c27f0e5a78. Report an issue: GitHub.