Hmbown/CodeWhale · error
fleet alert URL is not configured
Error message
fleet alert URL {name} is not configured What it means
required_https_url() resolves a Webhook alert adapter's url_env variable through the secret resolver (env var, non-empty required). If the variable is absent or empty, resolve returns None and this error fires, naming the expected env var. It fires before URL syntax or scheme checks, so a set-but-malformed URL produces a different error.
Solutions
- Export a non-empty HTTPS URL under the exact name in url_env in the process environment that dispatches alerts.
- Check 'env | grep <name>' inside that same context (systemd 'Environment=', tmux session, CI secret) rather than your editor shell.
- Keep dry_run=true in environments that intentionally have no webhook target — dry-run does not hit the URL requirement path the same way.
- If multiple adapters need the same endpoint, point all url_env fields at one variable name and set it once.
Example fix
# before
# adapters: {"hook":{"kind":"webhook","url_env":"ALERT_HOOK_URL"}}
$ ./run-fleet.sh # ALERT_HOOK_URL unset -> error
# after
$ ALERT_HOOK_URL="https://hooks.example.com/fleet" ./run-fleet.sh Defensive patterns
Strategy: validation
Validate before calling
fn webhook_urls_resolvable(cfg: &FleetAlertConfig) -> Result<(), String> {
for (name, adapter) in &cfg.adapters {
if let FleetAlertAdapterConfig::Webhook { url_env, .. } = adapter {
if std::env::var(url_env).map(|v| v.is_empty()).unwrap_or(true) {
return Err(format!("webhook adapter '{name}' needs non-empty env {url_env}"));
}
}
}
Ok(())
}
Try / catch
match dispatcher.dispatch(&event) {
Err(e) if e.to_string().contains("URL") && e.to_string().contains("not configured") => {
// env var named in message is missing in this process; log and degrade (skip alert)
}
other => other.map(|_| ())?,
} Prevention
- Centralize webhook URL env var names in one constants module referenced by config generation.
- Smoke-test env inheritance with 'env | grep <url_env>' inside the exact runtime context.
- Automate provisioning so every environment that enables webhooks sets the URL var.
- Consider a custom FleetAlertSecretResolver backed by a secret store so absence is detected at construction.
When it happens
Trigger: A Webhook adapter (kind: webhook with url_env: "FLEET_WEBHOOK_URL") dispatched with dry_run=false while FLEET_WEBHOOK_URL is unset, exported as empty, or defined only in a parent shell the process never inherited.
Common situations: Secrets managed per-shell but fleet runs from a daemon, launcher, or CI job; renaming the env var in config without updating deployment environment; multiple webhook adapters sharing one url_env name where one deployment forgot it.
Understand the failure class
Background: "environment variable is not set" and "Missing keys in environment" errors: what missing required env var messages mean and how to fix them — this error's family across 28 libraries.
Related errors
- fleet alert secret is not configured
- fleet alert URL from
- fleet alert adapter is not configured
- Fleet alert adapter is not configured
- Fleet alert secret is not configured
AI-assisted analysis of Hmbown/CodeWhale@0c42157ee5 (2026-08-20).
Data as JSON: /api/errors/b56724963868014e.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/src/fleet/alerts.rs:498
}
}
fn required_secret<R>(resolver: &R, name: &str) -> Result<String>
where
R: FleetAlertSecretResolver,
{
resolver
.resolve(name)
.ok_or_else(|| anyhow!("fleet alert secret {name} is not configured"))
}
fn required_https_url<R>(resolver: &R, name: &str) -> Result<String>
where
R: FleetAlertSecretResolver,
{
let url = resolver
.resolve(name)
.ok_or_else(|| anyhow!("fleet alert URL {name} is not configured"))?;
validate_https_alert_url(name, &url)?;
Ok(url)
}
fn validate_https_alert_url(name: &str, url: &str) -> Result<()> {
let parsed = reqwest::Url::parse(url)
.with_context(|| format!("fleet alert URL from {name} is not a valid URL"))?;
if parsed.scheme() != "https" {
return Err(anyhow!("fleet alert URL from {name} must use https"));
}
Ok(())
}
fn short_reason(reason: &str) -> String {
let trimmed = reason.trim();
if trimmed.len() <= 240 {
return trimmed.to_string();
}View on GitHub (pinned to 0c42157ee5)