{"record":{"id":"d354ff7198023a33","repo":"Hmbown/CodeWhale","slug":"fleet-alert-secret-name-is-not-configured","errorCode":null,"errorMessage":"fleet alert secret {name} is not configured","messagePattern":"fleet alert secret (.+?) is not configured","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/tui/src/fleet/alerts.rs","lineNumber":489,"sourceCode":"        FleetAlertEventClass::VerifierFailed => \"verifier_failed\",\n        FleetAlertEventClass::RunCompleted => \"run_completed\",\n    }\n}\n\nfn redacted_secret_header(secret_env: Option<&str>) -> Value {\n    match secret_env {\n        Some(name) => json!({ \"X-CodeWhale-Webhook-Secret\": redacted_env(name) }),\n        None => json!({}),\n    }\n}\n\nfn required_secret<R>(resolver: &R, name: &str) -> Result<String>\nwhere\n    R: FleetAlertSecretResolver,\n{\n    resolver\n        .resolve(name)\n        .ok_or_else(|| anyhow!(\"fleet alert secret {name} is not configured\"))\n}\n\nfn required_https_url<R>(resolver: &R, name: &str) -> Result<String>\nwhere\n    R: FleetAlertSecretResolver,\n{\n    let url = resolver\n        .resolve(name)\n        .ok_or_else(|| anyhow!(\"fleet alert URL {name} is not configured\"))?;\n    validate_https_alert_url(name, &url)?;\n    Ok(url)\n}\n\nfn validate_https_alert_url(name: &str, url: &str) -> Result<()> {\n    let parsed = reqwest::Url::parse(url)\n        .with_context(|| format!(\"fleet alert URL from {name} is not a valid URL\"))?;\n    if parsed.scheme() != \"https\" {\n        return Err(anyhow!(\"fleet alert URL from {name} must use https\"));","sourceCodeStart":471,"sourceCodeEnd":507,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/0c42157ee52f9d55af2b506d71b46249910f77d3/crates/tui/src/fleet/alerts.rs#L471-L507","documentation":"When a fleet alert adapter sends for real (not dry_run), required_secret() asks the FleetAlertSecretResolver for a named secret — by default FleetEnvSecretResolver, which reads an environment variable and treats empty strings as missing. A None resolve produces this error, naming the env var that was expected. It guards PagerDuty routing keys and webhook secret_env headers (X-CodeWhale-Webhook-Secret).","triggerScenarios":"Dispatching a real (dry_run=false) alert through PagerDuty with routing_key_env unset/empty, or a Webhook adapter whose secret_env variable is unset, empty, or only set in a different shell/session/service unit.","commonSituations":"Env var set in an interactive shell but not in the systemd/launchd/tmux environment the TUI runs under; variable name typo between config and environment; CI runners lacking secrets; values consisting only of whitespace (env resolver only filters truly empty strings, but shell 'export VAR=' sets empty).","solutions":["Set the named variable to a non-empty value in the exact environment the fleet dispatcher runs in: e.g. export PAGERDUTY_ROUTING_KEY=... (or match whatever name config references).","Verify spelling/case of the *_env field in the adapter config against the variable you exported; print the resolver name from the error to confirm.","If the variable is intentionally optional, remove the secret_env/routing_key_env field from the adapter config so required_secret is never called for it.","Run one dispatch with dry_run=true first — dry-run prepares payloads without resolving send-time secrets where avoidable, confirming the rest of the config."],"exampleFix":"# before\n# config: {\"kind\":\"pagerduty\",\"routing_key_env\":\"PAGERDUTY_KEY\"}\n$ codewhale fleet ...   # -> fleet alert secret PAGERDUTY_KEY is not configured\n\n# after\n$ export PAGERDUTY_KEY=\"deadbeef...\" && codewhale fleet ...","handlingStrategy":"validation","validationCode":"fn required_alert_env_vars(cfg: &FleetAlertConfig) -> Vec<String> {\n    let mut names = Vec::new();\n    if cfg.dry_run { return names; }\n    for adapter in cfg.adapters.values() {\n        match adapter {\n            FleetAlertAdapterConfig::PagerDuty { routing_key_env, .. } => names.push(routing_key_env.clone()),\n            FleetAlertAdapterConfig::Webhook { secret_env: Some(s), .. } => names.push(s.clone()),\n            _ => {}\n        }\n    }\n    names.retain(|n| std::env::var(n).map(|v| !v.is_empty()).unwrap_or(false));\n    names // non-empty = OK; diff against the full list to find missing ones\n}\n","typeGuard":null,"tryCatchPattern":"if let Err(e) = dispatcher.dispatch(&event) {\n    let msg = e.to_string();\n    if msg.contains(\"secret\") && msg.contains(\"not configured\") {\n        // parse the var name out of the message, prompt/operator to set it, skip alerting\n    } else { return Err(e); }\n}","preventionTips":["Set alert secrets in the service/daemon environment definition (systemd Environment=, CI secret, launcher env), not ad-hoc shells.","Prefer one *_env name per secret across all adapters to avoid per-route drift.","Add a startup probe that resolves every configured secret before the first dispatch.","Treat empty-string exports as unset — the resolver filters them."],"tags":["fleet","alerts","secrets","environment","env-var"],"backgroundTag":"missing-env-var","analyzedSha":"0c42157ee52f9d55af2b506d71b46249910f77d3","analyzedAt":"2026-08-20T21:50:45.477Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}