affaan-m/ECC · error · FalError

download requires HTTPS on an approved fal.media host

Error message

download requires HTTPS on an approved fal.media host

What it means

download() only fetches assets over HTTPS on hosts under fal.media (or a *.fal.media subdomain), with no embedded credentials and port 443 or default. This SSRF/asset-safety guard runs both before the initial request (via download) and on every redirect target (via _SafeRedirect.redirect_request); a URL that fails this check raises FalError.

Solutions

  1. Only pass fal.media HTTPS URLs to download; download third-party URLs with your own HTTP client instead
  2. If fal.ai now serves assets on a new domain, update the host allowlist in _validate_download_url to include it
  3. Strip any userinfo from the URL and use plain https on port 443
  4. Inspect redirect chains (curl -sIL) to confirm every hop stays on fal.media

Example fix

# before
download('https://cdn.other-provider.com/mesh.glb', dest)
# after
if 'fal.media' not in urlparse(url).hostname:
    raise ValueError('use a plain HTTP client for non-fal.media URLs')
download(url, dest)
Defensive patterns

Strategy: validation

Validate before calling

from urllib.parse import urlparse
host = urlparse(url).hostname or ''
assert urlparse(url).scheme == 'https' and (host == 'fal.media' or host.endswith('.fal.media'))

Type guard

def is_fal_media_url(url: str) -> bool:
    p = urlparse(url)
    h = p.hostname or ''
    return p.scheme == 'https' and (h == 'fal.media' or h.endswith('.fal.media'))

Try / catch

try:
    download(url, dest)
except FalError:
    # fall back to a generic client for non-fal.media assets
    import urllib.request; urllib.request.urlretrieve(url, dest)

Prevention

When it happens

Trigger: download('http://fal.media/x.mp4', ...) (plain HTTP); download from a non-fal.media host (e.g. a CDN URL returned by another provider or a signed s3/sr.se URL); a URL with embedded userinfo (https://user:pass@fal.media/...); a redirect issued by fal.media that points off-host to a different domain.

Common situations: Passing a URL from a different provider's CDN (e.g. replicate.delivery) into falapi.download; fal.ai introduces a new asset domain not yet allowlisted; testing against a localhost mirror; a model response contains a redirect to a third-party storage host.

Understand the failure class

Background: "Invalid URL" / "URL cannot be empty": fix the malformed or missing URL behind request-construction failures — this error's family across 50 libraries.

Related errors


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

Appendix: source

Thrown at skills/taste-application/scripts/taste/falapi.py:671

# ---------------------------------------------------------------------------
# download
# ---------------------------------------------------------------------------


MAX_DOWNLOAD_BYTES = 2 * 1024 * 1024 * 1024  # bounded large video/GLB downloads


def _validate_download_url(url: str) -> None:
    try:
        parsed = urllib.parse.urlsplit(url)
        host = parsed.hostname or ""
        valid = (parsed.scheme == "https" and not parsed.username
                 and not parsed.password and parsed.port in (None, 443)
                 and (host == "fal.media" or host.endswith(".fal.media")))
    except ValueError:
        valid = False
    if not valid:
        raise FalError("download requires HTTPS on an approved fal.media host")


class _SafeRedirect(urllib.request.HTTPRedirectHandler):
    def redirect_request(self, req, fp, code, msg, headers, newurl):
        _validate_download_url(newurl)
        return super().redirect_request(req, fp, code, msg, headers, newurl)


def download(url: str, dest: str | Path) -> Path:
    """Bounded HTTPS download; failed transfers preserve existing destinations."""
    dest = Path(dest)
    if is_dry_run():
        dest.parent.mkdir(parents=True, exist_ok=True)
        dest.write_bytes(b"taste-forge dry-run placeholder\n")
        log.info("[dry-run] would download from %s", safe_url(url))
        return dest

    require_live()

View on GitHub (pinned to 8321021c54)