affaan-m/ECC · error · FalError
download failed; existing destination preserved
Error message
download failed; existing destination preserved
What it means
download() wraps its whole download loop in a broad except: any non-FalError exception (network I/O error, DNS failure, timeout, disk full, permission error) is converted to FalError with the message 'download failed; existing destination preserved'. The handler re-raises FalError unchanged, and a finally block unlinks the temp file, so a failed download never clobbers a pre-existing destination file.
Solutions
- Check that the destination directory exists and is writable; create the parent with Path(dest).parent.mkdir(parents=True, exist_ok=True).
- Test connectivity to the fal.ai/CDN host (curl -I the URL).
- Free disk space if the drive is full — large media downloads commonly hit ENOSPC.
- Retry with backoff; transient network faults are the dominant cause.
- Inspect the suppressed underlying exception by reproducing the fetch outside download() (e.g. urllib) to see the real cause, since the handler hides it with `from None`.
Example fix
// before
dest.parent.mkdir(parents=True, exist_ok=True) # missing
api.download(url, dest)
// after
dest = Path(dest)
dest.parent.mkdir(parents=True, exist_ok=True)
try:
api.download(url, dest)
except api.FalError as e:
print('download failed, previous file kept:', e) Defensive patterns
Strategy: try-catch
Validate before calling
from pathlib import Path
import shutil
def can_download_to(dest):
dest = Path(dest)
dest.parent.mkdir(parents=True, exist_ok=True)
if not os.access(dest.parent, os.W_OK):
return False
return shutil.disk_usage(dest.parent).free > MIN_FREE_BYTES Try / catch
try:
api.download(url, dest)
except api.FalError as e:
log.warning('download failed, existing file kept: %s', e)
# fall back to cached dest or re-raise Prevention
- Ensure the destination parent directory exists and is writable before calling.
- Check free disk space before large media downloads.
- Verify connectivity to the host with a cheap HEAD request first.
- Remember the API hides the root cause (from None) — reproduce with curl/urllib when diagnosing.
When it happens
Trigger: Any exception during download(url, dest) other than the explicit size/limit checks: socket timeouts, connection reset, HTTP errors surfaced as exceptions, unwritable temp directory, disk-full during fh.write, or os.replace failing.
Common situations: Server outage or 5xx from the CDN; no internet/DNS in a container; full disk when saving large video files; read-only working directory; wrong destination path (parent missing).
Understand the failure class
Background: 'Something went wrong' / 'Request failed (500)' / 'HTTP error! status: 404' — what failed HTTP requests actually mean and how to find the real cause — this error's family across 28 libraries.
Related errors
- download length does not match declared size
- download failed; existing destination preserved
- download length does not match declared size
- fal call failed after one attempt; job acceptance may be…
- fal call failed after one attempt; job acceptance may be…
AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16).
Data as JSON: /api/errors/1bebc100c3dc9250.
Report an issue: GitHub.
Appendix: source
Thrown at skills/taste-application/scripts/falapi.py:721
delete=False) as fh:
temporary = Path(fh.name)
total = 0
while True:
chunk = resp.read(min(1024 * 1024, MAX_DOWNLOAD_BYTES - total + 1))
if not chunk:
break
total += len(chunk)
if total > MAX_DOWNLOAD_BYTES:
raise FalError("download exceeds maximum allowed size")
fh.write(chunk)
if expected is not None and total != expected:
raise FalError("download length does not match declared size")
os.replace(temporary, dest)
temporary = None
except FalError:
raise
except Exception:
raise FalError("download failed; existing destination preserved") from None
finally:
if temporary is not None:
temporary.unlink(missing_ok=True)
return dest
# ---------------------------------------------------------------------------
# dry-run stubs
# ---------------------------------------------------------------------------
def _digest(*parts: Any) -> str:
h = hashlib.sha256("|".join(str(p) for p in parts).encode("utf-8"))
return h.hexdigest()[:12]
def _stub_from_schema(schema: dict) -> dict:
"""Build a stub object with the same keys and types as ``schema``."""View on GitHub (pinned to 8321021c54)