affaan-m/ECC · error · FalError

cannot upload, file does not exist

Error message

cannot upload, file does not exist: {p}

What it means

falapi.upload() validates that the local file exists before uploading it to fal.ai storage. If Path(path).exists() is false, it raises FalError with the resolved path, preventing a wasted POST against the provider. In dry-run mode the existence check still applies to live uploads while dry runs are exempt from require_live().

Solutions

  1. Fix the path: print/inspect the resolved path in the error and correct the filename, extension, or working directory (use absolute paths where possible).
  2. Check the file exists before calling: `from pathlib import Path; assert Path(p).is_file()` or guard in your pipeline before upload_many().
  3. Regenerate the missing artifact first (e.g. re-run the step that produced the file) if it was cleaned up or never written.
  4. On Linux/macOS verify case matches exactly and that symlinks resolve (`ls -l`, `readlink -f`).

Example fix

# before
url = upload("outputs/asset.glb")  # FalError: cannot upload, file does not exist: outputs/asset.glb

# after
from pathlib import Path
p = Path("outputs/asset.glb").resolve()
if not p.is_file():
    raise FileNotFoundError(f"regenerate artifact first: {p}")
url = upload(str(p))
Defensive patterns

Strategy: validation

Validate before calling

from pathlib import Path
p = Path(path)
if not p.is_file():
    raise FileNotFoundError(f"artifact missing before upload: {p.resolve()}")

Type guard

def is_uploadable(path: object) -> bool:
    from pathlib import Path
    return isinstance(path, (str, Path)) and Path(path).is_file()

Try / catch

try:
    url = upload(path)
except FalError as e:
    if str(e).startswith("cannot upload, file does not exist"):
        raise FileNotFoundError(path) from e
    raise

Prevention

When it happens

Trigger: Calling upload() (or upload_many() which delegates to it) with a path string that does not exist on disk — relative path resolved from the wrong working directory, file deleted between generation and upload, or a wrong extension/typo in the filename.

Common situations: Passing a path generated in a previous pipeline run whose temp/outputs directory was cleaned; running the script from a different cwd than expected so relative paths break; case-sensitivity mismatch (Linux vs macOS); symlink targets that were removed; snapshot files not yet written when upload is scheduled.

Understand the failure class

Background: "File not found" and ENOENT errors: why libraries can't find a file that should exist — this error's family across 50 libraries.

Related errors


AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16). Data as JSON: /api/errors/3103d95c0391a580. Report an issue: GitHub.

Appendix: source

Thrown at skills/taste-application/scripts/taste/falapi.py:300


def _cache_key(path: Path) -> tuple[str, int, int]:
    st = path.stat()
    return (str(path.resolve()), st.st_mtime_ns, st.st_size)


def upload(path: str | Path) -> str:
    """Upload a local file and return its URL, memoized per (path, mtime, size).

    apply.py reuses the same handful of stills across every shot in a run and
    across concurrent workers; without this cache each of those becomes a
    redundant multi-megabyte POST.
    """
    if not is_dry_run():
        require_live()
    p = Path(path)
    if not p.exists():
        raise FalError(f"cannot upload, file does not exist: {p}")

    key = _cache_key(p)
    with _UPLOAD_LOCK:
        hit = _UPLOAD_CACHE.get(key)
    if hit and (is_dry_run() == hit.startswith(DRY_RUN_HOST + "/")):
        log.debug("upload cache hit: %s", p.name)
        return hit

    if is_dry_run():
        url = f"{DRY_RUN_HOST}/uploads/{_digest(str(key))}/{p.name}"
        log.info("[dry-run] would upload %s (%d bytes) -> %s", p, key[2], url)
    else:
        api_key()
        try:
            url = _fal().upload_file(str(p))
        except Exception:
            raise FalError("fal upload failed; provider details omitted") from None
        log.info("uploaded %s -> %s", p.name, safe_url(url))

View on GitHub (pinned to 8321021c54)