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

falapi._validate_download_url enforces that any URL opened for downloading a generated asset uses HTTPS with no embedded credentials, port 443 or default, and a host of fal.media or a subdomain. urllib's opener and its redirect handler both call it, so even a 30x to a disallowed host is rejected. This prevents SSRF and mixed-content downloads from attacker-influenced redirect targets.

Solutions

  1. Use the exact HTTPS URL returned by the fal API response, unmodified
  2. Ensure the URL scheme is https and there is no userinfo or explicit port
  3. If the host legitimately changed, whitelist-check the new host against fal.media before calling download
  4. Catch FalError and log the offending URL (sanitized) to diagnose which constraint failed

Example fix

// before
download("http://fal.media/files/abc.mp4", dest)
// after
download("https://fal.media/files/abc.mp4", dest)
Defensive patterns

Strategy: validation

Validate before calling

from urllib.parse import urlparse
def is_downloadable(u):
    try:
        p = urlparse(u)
        return p.scheme == "https" and not p.username and not p.password \
            and p.port in (None, 443) and (p.hostname == "fal.media" or p.hostname.endswith(".fal.media"))
    except ValueError:
        return False

Prevention

When it happens

Trigger: Calling download() with an http:// URL, a URL whose host is not fal.media (e.g. a CDN host like fal-cdn.example.com), a URL containing user:pass@, an explicit non-443 port, or a malformed URL; also automatically when the server redirects to any such URL.

Common situations: Hard-coding an http:// link copied from an old integration; fal changing media hosts so stored URLs point elsewhere; constructing the URL by string concatenation that injects credentials; a redirect chain bouncing off fal.media to a generic CDN.

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/6fcdbaf42b96002a. Report an issue: GitHub.

Appendix: source

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