zeroclaw-labs/zeroclaw · error · anyhow::Error
Generated image URL must use HTTPS
Error message
Generated image URL must use HTTPS
What it means
parse_public_https_url only accepts https URLs for generated image downloads. After reqwest parses the URL, any scheme other than https (typically http) is rejected. The rule exists because the image URL is fetched server-side with DNS pinning and SSRF checks, and the tool refuses to pull image bytes over a plaintext connection.
Source
Thrown at crates/zeroclaw-tools/src/image_gen.rs:32
const FAL_ERROR_LIMIT_BYTES: usize = 16 * 1024;
const GENERATED_IMAGE_LIMIT_BYTES: usize = 20 * 1024 * 1024;
const MAX_IMAGE_REDIRECTS: usize = 10;
struct ValidatedImageTarget {
url: reqwest::Url,
host: String,
resolved_addrs: Vec<SocketAddr>,
}
fn parse_public_https_url(raw_url: &str) -> anyhow::Result<(reqwest::Url, String, u16)> {
let raw_url = raw_url.trim();
if raw_url.is_empty() || raw_url.chars().any(char::is_whitespace) {
anyhow::bail!("Generated image URL must be a non-empty URL without whitespace");
}
let mut url = reqwest::Url::parse(raw_url).context("Invalid generated image URL")?;
if url.scheme() != "https" {
anyhow::bail!("Generated image URL must use HTTPS");
}
if !url.username().is_empty() || url.password().is_some() {
anyhow::bail!("Generated image URL userinfo is not allowed");
}
let request_host = url
.host_str()
.ok_or_else(|| anyhow::Error::msg("Generated image URL must include a host"))?;
if request_host.ends_with('.') {
anyhow::bail!("Generated image URL host must not end with a dot");
}
let host = domain_guard::normalize_domain(request_host)
.ok_or_else(|| anyhow::Error::msg("Generated image URL host is invalid"))?;
let ip_literal = host.parse::<IpAddr>().ok();
if domain_guard::is_private_or_local_host(&host) {
anyhow::bail!("Generated image URL targets a local or non-global host");
}
if ip_literal.is_some_and(domain_guard::is_cloud_metadata_ip) {View on GitHub (pinned to 88bb9c8533)
Solutions
- Serve the image host over HTTPS: put the fal storage or custom domain behind a TLS certificate.
- If using fal-managed storage (v3.fal.media), prefer it — it always returns https URLs.
- If the same host also serves https, the producer of the URL should emit the https form; the tool itself will not silently upgrade http to https.
Example fix
# before image url: http://storage.internal.local/files/img.png # rejected # after image url: https://cdn.example.com/files/img.png # accepted
Defensive patterns
Strategy: validation
Validate before calling
fn is_https_url(raw: &str) -> bool {
reqwest::Url::parse(raw.trim()).map(|u| u.scheme() == "https").unwrap_or(false)
} Type guard
fn is_valid_image_url(u: &str) -> bool { is_non_empty_url(u) && is_https_url(u) } Try / catch
if let Err(e) = download_generated_image(target).await {
if e.to_string().contains("must use HTTPS") {
// storage is emitting http links: fix TLS at the origin, do not downgrade
}
} Prevention
- Put custom storage/gateways behind TLS certificates so every emitted link is https.
- Assert https on provider URLs in integration tests to catch scheme regressions early.
When it happens
Trigger: The fal.ai response carries an http:// image URL: a self-hosted fal gateway or custom storage domain without TLS, a storage configuration that emits plain-http links, or a hand-crafted/test URL passed as http://.
Common situations: Self-hosting fal.ai or pointing it at custom object storage (MinIO, internal S3) that is not behind TLS, local development setups where everything is http, and providers that switch schemes after a storage backend change.
Related errors
- Generated image URL userinfo is not allowed
- Refusing to transmit sensitive data over non-HTTPS URL: URL
- URL must include a valid host
- Generated image URL must be a non-empty URL without whitespa
- Generated image URL host must not end with a dot
AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23).
Data as JSON: /api/errors/8897050e7c7faa6f.
Report an issue: GitHub.