affaan-m/ECC · error · FalError
download length does not match declared size
Error message
download length does not match declared size
What it means
download() in falapi.py streams the remote file to a temp file while counting bytes. If the server declared a Content-Length and the streamed total differs from it, the file is incomplete or corrupted, so download() raises FalError instead of promoting a truncated/corrupt file to the destination. The temp file is removed and the existing destination is left untouched.
Solutions
- Re-run the download; transient truncation is usually fixed on retry.
- Check network stability / disable proxies or VPNs that can truncate or re-encode responses.
- Verify the server's Content-Length matches the actual resource size (curl -I) and compare with the expected value passed in.
- If the source systematically reports a wrong size, fetch the resource from a mirror or updated URL.
Example fix
# before (fragile, no retry)
api.download(url, dest, expected=size)
# after (bounded retry for transient truncation)
for attempt in range(3):
try:
api.download(url, dest, expected=size)
break
except api.FalError:
if attempt == 2:
raise
time.sleep(2 ** attempt) Defensive patterns
Strategy: retry
Validate before calling
import os
head = urllib.request.urlopen(url)
expected = int(head.headers.get('Content-Length', 0))
if expected > MAX_ALLOWED:
raise ValueError('file too large')
os.remove(dest) if os.path.exists(dest) else None Try / catch
for attempt in range(3):
try:
api.download(url, dest, expected=size)
break
except api.FalError as e:
if attempt == 2 or 'exceeds' in str(e):
raise
time.sleep(2 ** attempt) Prevention
- Retry downloads with exponential backoff; truncation is usually transient.
- Check Content-Length with a HEAD request before downloading large media.
- Avoid unreliable proxies/VPNs for large transfers.
- Keep the existing destination file — the API preserves it precisely so retries are safe.
When it happens
Trigger: Calling download(url, dest, expected=<size>) (or downloading from a URL whose Content-Length is known) when the connection drops mid-stream, the server sends fewer/more bytes than declared, or a proxy rewrites the body without updating Content-Length.
Common situations: Flaky networks or Wi-Fi drops during a large fal.ai media download; CDN edge truncation; content-encoding (gzip) double-counting bytes; server-side timeouts on slow links.
Understand the failure class
Background: Checksum mismatch errors: "checksum verification failed", "digest mismatch", "expected vs actual checksum" — what they mean and how to fix them — this error's family across 41 libraries.
Related errors
- download failed; existing destination preserved
- download length does not match declared size
- application bundle differs from its bound evidence
- download failed; existing destination preserved
- download length does not match declared size
AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16).
Data as JSON: /api/errors/dabc448ff5444619.
Report an issue: GitHub.
Appendix: source
Thrown at skills/taste-application/scripts/falapi.py:715
with opener.open(req, timeout=300) as resp:
declared = getattr(resp, "headers", {}).get("Content-Length")
expected = int(declared) if declared is not None else None
if expected is not None and not 0 <= expected <= MAX_DOWNLOAD_BYTES:
raise FalError("download declares an invalid or excessive size")
with tempfile.NamedTemporaryFile(dir=dest.parent, prefix=".taste-download-",
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:View on GitHub (pinned to 8321021c54)