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
- Use the exact HTTPS URL returned by the fal API response, unmodified
- Ensure the URL scheme is https and there is no userinfo or explicit port
- If the host legitimately changed, whitelist-check the new host against fal.media before calling download
- 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
- Always use URLs exactly as returned by the fal API response object
- Never string-build media URLs by hand
- Keep a pre-flight urlparse check in your download helper
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
- remote instinct imports require https URLs
- download requires HTTPS on an approved fal.media host
- remote import exceeds
- remote import host resolves to a non-public address
- remote import URL is missing a hostname
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)