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
- Only pass fal.media HTTPS URLs to download; download third-party URLs with your own HTTP client instead
- If fal.ai now serves assets on a new domain, update the host allowlist in _validate_download_url to include it
- Strip any userinfo from the URL and use plain https on port 443
- 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
- Only feed falapi.download URLs that came from fal.ai responses
- Route third-party CDN URLs through your own HTTP client
- Strip credentials and force port 443 on asset URLs
- Watch for fal.ai asset-domain changes and update the allowlist
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
- artifact path escapes output directory
- artifact permits provider execution
- download requires HTTPS on an approved fal.media host
- invalid Discord webhook URL
- Invalid managed hook handler at
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)