{"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/8880682c63083a91624de936797efa3ce9e498fd/crates/tui/src/fleet/alerts.rs#L115-L151","documentation":"`FleetAlertDispatcher::dispatch` matches routes to event classes, then looks up `route.adapter` in `config.adapters` (fleet/alerts.rs:133). A route naming an adapter key absent from the adapters map is a configuration error and aborts dispatch for that route.","triggerScenarios":"A route says `adapter: \"slack\"` while the adapters map only defines `webhook`; an adapter was renamed without updating routes; the adapter block was dropped in an environment-specific config.","commonSituations":"Config drift between environments; hand-edited fleet alert config; route templates copied without matching adapter definitions.","solutions":["Add an adapters entry whose key exactly equals `route.adapter`","Or repoint the route at an adapter key that exists","Cross-validate routes against adapters at config load so the mismatch surfaces before any event fires"],"exampleFix":"// before\n\"routes\": [{\"events\": [], \"adapter\": \"slack\"}],\n\"adapters\": {\"webhook\": {\"type\": \"webhook\"}}\n// after\n\"routes\": [{\"events\": [], \"adapter\": \"slack\"}],\n\"adapters\": {\"slack\": {\"type\": \"webhook\"}}","handlingStrategy":"validation","validationCode":"for route in &config.routes {\n    anyhow::ensure!(\n        config.adapters.contains_key(&route.adapter),\n        \"route references unknown adapter '{}'\",\n        route.adapter\n    );\n}","typeGuard":"fn all_routes_have_adapters(config: &FleetAlertConfig) -> bool {\n    config.routes\n        .iter()\n        .all(|r| config.adapters.contains_key(&r.adapter))\n}","tryCatchPattern":null,"preventionTips":["Validate alert config at load time, not at event time","Exercise dispatch with dry_run in CI to catch config drift","Rename adapters and routes together in one change"],"tags":["config","alerts","fleet","routing"],"backgroundTag":null,"analyzedSha":"8880682c63083a91624de936797efa3ce9e498fd","analyzedAt":"2026-08-16T11:31:27.956Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}