{"record":{"id":"5c286cb934fd60f2","repo":"Hmbown/CodeWhale","slug":"fleet-alert-adapter-is-not-configured","errorCode":null,"errorMessage":"fleet alert adapter {} is not configured","messagePattern":"fleet alert adapter (.+?) is not configured","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/tui/src/fleet/alerts.rs","lineNumber":133,"sourceCode":"    R: FleetAlertSecretResolver,\n{\n    pub fn new(config: FleetAlertConfig, resolver: R) -> Self {\n        Self { config, resolver }\n    }\n\n    pub fn dispatch(&self, event: &FleetAlertEvent) -> Result<Vec<FleetAlertDelivery>> {\n        if !self.config.enabled {\n            return Ok(Vec::new());\n        }\n        let mut deliveries = Vec::new();\n        for route in self\n            .config\n            .routes\n            .iter()\n            .filter(|route| route_matches(route, event.class))\n        {\n            let adapter = self.config.adapters.get(&route.adapter).ok_or_else(|| {\n                anyhow!(\"fleet alert adapter {} is not configured\", route.adapter)\n            })?;\n            let prepared = prepare_alert(&route.adapter, adapter, event, self.config.dry_run)?;\n            let sent = if self.config.dry_run {\n                false\n            } else {\n                send_alert(adapter, &prepared.body, &self.resolver)?\n            };\n            deliveries.push(FleetAlertDelivery {\n                adapter: route.adapter.clone(),\n                event_class: event.class,\n                dry_run: self.config.dry_run,\n                sent,\n                redacted_payload: prepared.redacted_payload,\n            });\n        }\n        Ok(deliveries)\n    }\n}","sourceCodeStart":115,"sourceCodeEnd":151,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/0c42157ee52f9d55af2b506d71b46249910f77d3/crates/tui/src/fleet/alerts.rs#L115-L151","documentation":"FleetAlertDispatcher::dispatch() routes each FleetAlertEvent through config.routes whose event class matches. Each route names an adapter by string key, and that key must exist in the config.adapters map (kind: slack | webhook | pagerduty entries). If the route's adapter string has no matching adapters entry, dispatch fails immediately with this error.","triggerScenarios":"A FleetAlertConfig where routes[].adapter (e.g. \"ops-slack\") does not match any key in the adapters BTreeMap — typo, renamed adapter, route copied from another config, or adapter entry deleted while the route stayed.","commonSituations":"Hand-edited fleet alert TOML/JSON config; renaming an adapter key without updating routes; enabling alert routes via config merge where the adapters section comes from a different file; CI configs drifting from local ones.","solutions":["Open the fleet alerts config and make the route's adapter string byte-identical to an existing key under adapters.","If the adapter was meant to exist, add the missing adapters entry (Slack{webhook_env}, Webhook{url_env,secret_env}, or PagerDuty{routing_key_env}).","Remove the stale route if the adapter was retired.","Until config is fixed, you can set enabled=false or use dry_run=true so dispatch returns early instead of erroring."],"exampleFix":"// before\n{\"enabled\":true,\"routes\":[{\"events\":[\"task_failed\"],\"adapter\":\"slack-ops\"}],\"adapters\":{\"slack\":{\"kind\":\"slack\",\"webhook_env\":\"SLACK_WEBHOOK_URL\"}}}\n\n// after (route key matches adapters key)\n{\"enabled\":true,\"routes\":[{\"events\":[\"task_failed\"],\"adapter\":\"slack\"}],\"adapters\":{\"slack\":{\"kind\":\"slack\",\"webhook_env\":\"SLACK_WEBHOOK_URL\"}}}","handlingStrategy":"validation","validationCode":"fn validate_alert_config(cfg: &FleetAlertConfig) -> Result<(), String> {\n    for route in &cfg.routes {\n        if !cfg.adapters.contains_key(&route.adapter) {\n            return Err(format!(\"route references unknown adapter '{}'\", route.adapter));\n        }\n    }\n    Ok(())\n}\n\nvalidate_alert_config(&config)?; // before enabling dispatch\n","typeGuard":"fn adapter_is_configured(cfg: &FleetAlertConfig, route: &FleetAlertRoute) -> bool {\n    cfg.adapters.contains_key(&route.adapter)\n}\n","tryCatchPattern":"match dispatcher.dispatch(&event) {\n    Ok(deliveries) => { /* record deliveries */ }\n    Err(e) if e.to_string().contains(\"adapter\") && e.to_string().contains(\"not configured\") => {\n        // config defect: log route/adapter names, disable the route, continue the run\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Derive route adapter names from the adapters map (iterate keys) instead of typing them twice.","Add a config-load-time assertion that every routes[].adapter key exists in adapters.","Keep alerts config in one file; merging routes and adapters from different sources invites drift.","Run one dry_run=true dispatch when changing alert config."],"tags":["fleet","alerts","config","adapter-routing"],"backgroundTag":"config-missing-reference","analyzedSha":"0c42157ee52f9d55af2b506d71b46249910f77d3","analyzedAt":"2026-08-20T21:50:45.477Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}