{"record":{"id":"d86265261f33f937","repo":"affaan-m/ECC","slug":"download-failed-existing-destination-preserved-falapi","errorCode":null,"errorMessage":"download failed; existing destination preserved","messagePattern":"download failed; existing destination preserved","errorType":"exception","errorClass":"FalError","httpStatus":null,"severity":"error","filePath":"skills/taste-application/scripts/taste/falapi.py","lineNumber":721,"sourceCode":"                                             delete=False) as fh:\n                temporary = Path(fh.name)\n                total = 0\n                while True:\n                    chunk = resp.read(min(1024 * 1024, MAX_DOWNLOAD_BYTES - total + 1))\n                    if not chunk:\n                        break\n                    total += len(chunk)\n                    if total > MAX_DOWNLOAD_BYTES:\n                        raise FalError(\"download exceeds maximum allowed size\")\n                    fh.write(chunk)\n                if expected is not None and total != expected:\n                    raise FalError(\"download length does not match declared size\")\n        os.replace(temporary, dest)\n        temporary = None\n    except FalError:\n        raise\n    except Exception:\n        raise FalError(\"download failed; existing destination preserved\") from None\n    finally:\n        if temporary is not None:\n            temporary.unlink(missing_ok=True)\n    return dest\n\n\n# ---------------------------------------------------------------------------\n# dry-run stubs\n# ---------------------------------------------------------------------------\n\n\ndef _digest(*parts: Any) -> str:\n    h = hashlib.sha256(\"|\".join(str(p) for p in parts).encode(\"utf-8\"))\n    return h.hexdigest()[:12]\n\n\ndef _stub_from_schema(schema: dict) -> dict:\n    \"\"\"Build a stub object with the same keys and types as ``schema``.\"\"\"","sourceCodeStart":703,"sourceCodeEnd":739,"githubUrl":"https://github.com/affaan-m/ECC/blob/8321021c54d670126ce3b2969d5deb880b4b0c2a/skills/taste-application/scripts/taste/falapi.py#L703-L739","documentation":"download() writes to a temporary file in the destination directory and only atomically renames it onto dest after a fully successful transfer. Any non-FalError exception during the request or write (network error, DNS failure, permission error, decode error) is converted into this generic FalError; because the rename never happened, the pre-existing destination file is preserved untouched.","triggerScenarios":"DNS resolution failure, connection reset/timeouts during opener.open or resp.read, SSL errors, permission denied writing into dest.parent, disk-full while writing the temp file, or urllib raising any unexpected exception.","commonSituations":"Offline/flaky network during a pipeline run; destination directory is read-only or owned by another user; long download exceeding the hardcoded timeout=300; antivirus or OS locking temp files in dest.parent.","solutions":["Check network connectivity and retry the download","Verify dest.parent exists and is writable by the current user (mkdir -p, chmod)","Increase the hardcoded timeout=300 in download() for large assets","Run the underlying urllib opener manually to see the original exception (this handler discards it via 'from None')"],"exampleFix":"# before\npath = download(url, dest)  # opaque 'download failed' on any error\n# after\ntry:\n    path = download(url, dest)\nexcept FalError:\n    dest.parent.mkdir(parents=True, exist_ok=True)\n    import urllib.request\n    urllib.request.urlretrieve(url, '/tmp/dbg-asset')  # surface real error\n    raise","handlingStrategy":"try-catch","validationCode":"assert dest.parent.exists() and os.access(dest.parent, os.W_OK), 'destination dir must be writable'","typeGuard":null,"tryCatchPattern":"try:\n    download(url, dest)\nexcept FalError as e:\n    if 'download failed' in str(e):\n        # real cause hidden; retry with a plain client to surface it\n        urllib.request.urlretrieve(url, dest.with_suffix('.tmp'))\n    raise","preventionTips":["Ensure the destination directory exists and is writable before calling","Raise the hardcoded 300s timeout for large assets by patching download()","Keep network retries around all download calls","Remember the old destination file is preserved — check its mtime to know whether the new download succeeded"],"tags":["download","network","file-io","error-handling"],"backgroundTag":"http-request-failed","analyzedSha":"8321021c54d670126ce3b2969d5deb880b4b0c2a","analyzedAt":"2026-09-16T10:08:13.343Z","contentChangedAt":"2026-09-16T10:08:13.343Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}