affaan-m/ECC · error · FalError

download length does not match declared size

Error message

download length does not match declared size

What it means

After the body is fully read, download() compares the number of bytes received against the Content-Length the server declared and raises if they differ. This detects truncated or padded transfers so a corrupt file is never renamed into place.

Solutions

  1. Retry the download; transient truncation is the most common cause
  2. Check network stability / disable interfering proxies or TLS-inspecting middleboxes
  3. Compare Content-Length with a HEAD request to see if the server is inconsistent
  4. Rely on the destination file being preserved (temp file is discarded) and surface a user-facing retry

Example fix

// before
download(url, dest)  # raises on truncated transfer
// after
for attempt in range(3):
    try:
        download(url, dest); break
    except FalError as e:
        if "does not match declared size" not in str(e): raise
Defensive patterns

Strategy: retry

Try / catch

for attempt in range(3):
    try:
        download(url, dest); break
    except FalError as e:
        if "does not match declared size" in str(e) and attempt < 2:
            time.sleep(2 ** attempt); continue
        raise

Prevention

When it happens

Trigger: Connection drops mid-transfer (urllib may surface EOF without error but fewer bytes), server declares a larger Content-Length than it sends, or an intermediary rewrites the body while keeping the original header.

Common situations: Flaky mobile/VPN connections truncating downloads; reverse proxies (e.g. misconfigured gzip) altering body length; CDN edge 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


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

Appendix: source

Thrown at skills/taste-distillation/scripts/taste/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)