{"record":{"id":"332b40cf9e177007","repo":"hasura/graphql-engine","slug":"duplicate-alternative-mode-identifier-0","errorCode":null,"errorMessage":"Duplicate alternative mode identifier: '{0}'","messagePattern":"Duplicate alternative mode identifier: '(.+?)'","errorType":"error_code","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"v3/crates/auth/hasura-authn/src/lib.rs","lineNumber":293,"sourceCode":"\nimpl Warning {\n    pub fn should_be_an_error(&self, flags: &open_dds::flags::OpenDdFlags) -> bool {\n        match self {\n            Warning::InvalidHeaderName(_) | Warning::InvalidHeaderValue(_, _) => {\n                flags.contains(open_dds::flags::Flag::DisallowInvalidHeadersInAuthConfig)\n            }\n            _ => false,\n        }\n    }\n}\n\n#[derive(Debug, thiserror::Error, PartialEq)]\npub enum Error {\n    #[error(\"Invalid URL for auth webhook: {0}\")]\n    InvalidAuthWebhookUrl(String),\n    #[error(\"{0}\")]\n    AuthConfigWarningsAsErrors(SeparatedBy<Warning>),\n    #[error(\"Duplicate alternative mode identifier: '{0}'\")]\n    DuplicateAlternativeModeIdentifier(String),\n}\n\n// A small utility type which exists for the sole purpose of displaying a vector with a certain\n// separator.\n#[derive(Debug, PartialEq)]\npub struct SeparatedBy<T> {\n    pub lines_of: Vec<T>,\n    pub separator: String,\n}\n\nimpl<T: Display> Display for SeparatedBy<T> {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        for (index, elem) in self.lines_of.iter().enumerate() {\n            elem.fmt(f)?;\n            if index < self.lines_of.len() - 1 {\n                self.separator.fmt(f)?;\n            }","sourceCodeStart":275,"sourceCodeEnd":311,"githubUrl":"https://github.com/hasura/graphql-engine/blob/724551b9ae87845594ef0408cff0e50eb6c90dc5/v3/crates/auth/hasura-authn/src/lib.rs#L275-L311","documentation":"Hard error from auth config generation: two entries in the auth config declared the same identifier for an alternative auth mode, but identifiers must be unique so the engine can unambiguously select a mode.","triggerScenarios":"The `modes`/alternative modes list of the auth config contains two entries whose mode identifier strings are equal (case-sensitive comparison of the configured identifiers).","commonSituations":"Copy-pasting an auth mode block and forgetting to rename its identifier, or merging auth configs from multiple teams that both defined a mode like \"admin\".","solutions":["Find the duplicated identifier in the alternative modes list and rename one entry to a unique value","Grep the auth config for the identifier shown in the message to locate both occurrences","Add a CI lint/duplication check over mode identifiers before merging config changes"],"exampleFix":"// before\nmodes:\n  - id: \"admin\"\n    ...\n  - id: \"admin\"\n    ...\n// after\nmodes:\n  - id: \"admin\"\n    ...\n  - id: \"admin-backup\"\n    ...","handlingStrategy":"validation","validationCode":"use std::collections::HashSet;\nlet mut ids = HashSet::new();\nfor mode in &auth_config.modes {\n    if !ids.insert(mode.identifier.clone()) {\n        return Err(format!(\"duplicate alternative mode identifier: {}\", mode.identifier));\n    }\n}","typeGuard":"fn modeIdsUnique(modes: &[Mode]) -> bool {\n    let mut s = std::collections::HashSet::new();\n    modes.iter().all(|m| s.insert(m.identifier.clone()))\n}","tryCatchPattern":null,"preventionTips":["Namespace mode identifiers per team (e.g. teamA-admin)","Run a duplicate-identifier lint on auth configs in CI","Rename rather than copy-paste mode blocks"],"tags":["auth","duplicate-key","config-validation","auth-modes"],"backgroundTag":"duplicate-identifier-in-config","analyzedSha":"724551b9ae87845594ef0408cff0e50eb6c90dc5","analyzedAt":"2026-08-28T07:32:55.105Z","schemaVersion":2},"datasetVersion":"2026-08-28T11:17:15.048Z"}