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
- Fix the path: print/inspect the resolved path in the error and correct the filename, extension, or working directory (use absolute paths where possible).
- Check the file exists before calling: `from pathlib import Path; assert Path(p).is_file()` or guard in your pipeline before upload_many().
- Regenerate the missing artifact first (e.g. re-run the step that produced the file) if it was cleaned up or never written.
- 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
- Use absolute, resolved paths when calling upload()/upload_many()
- Assert artifact existence immediately after the generating step, before scheduling the upload
- Be aware of cwd differences between dev shell, CI, and containers when using relative paths
- On Linux, double-check filename case; on cleanup jobs, don't delete outputs before their upload completes
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
- application bundle differs from its bound evidence
- At least one video is required
- --config-dir must be an existing absolute directory.
- --cwd must be an absolute path.
- --cwd must not contain a NUL byte.
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)