hasura/graphql-engine · error · Error

Duplicate alternative mode identifier: '{0}'

Error message

Duplicate alternative mode identifier: '{0}'

What it means

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.

Source

Thrown at v3/crates/auth/hasura-authn/src/lib.rs:293

impl Warning {
    pub fn should_be_an_error(&self, flags: &open_dds::flags::OpenDdFlags) -> bool {
        match self {
            Warning::InvalidHeaderName(_) | Warning::InvalidHeaderValue(_, _) => {
                flags.contains(open_dds::flags::Flag::DisallowInvalidHeadersInAuthConfig)
            }
            _ => false,
        }
    }
}

#[derive(Debug, thiserror::Error, PartialEq)]
pub enum Error {
    #[error("Invalid URL for auth webhook: {0}")]
    InvalidAuthWebhookUrl(String),
    #[error("{0}")]
    AuthConfigWarningsAsErrors(SeparatedBy<Warning>),
    #[error("Duplicate alternative mode identifier: '{0}'")]
    DuplicateAlternativeModeIdentifier(String),
}

// A small utility type which exists for the sole purpose of displaying a vector with a certain
// separator.
#[derive(Debug, PartialEq)]
pub struct SeparatedBy<T> {
    pub lines_of: Vec<T>,
    pub separator: String,
}

impl<T: Display> Display for SeparatedBy<T> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        for (index, elem) in self.lines_of.iter().enumerate() {
            elem.fmt(f)?;
            if index < self.lines_of.len() - 1 {
                self.separator.fmt(f)?;
            }

View on GitHub (pinned to 724551b9ae)

Solutions

  1. Find the duplicated identifier in the alternative modes list and rename one entry to a unique value
  2. Grep the auth config for the identifier shown in the message to locate both occurrences
  3. Add a CI lint/duplication check over mode identifiers before merging config changes

Example fix

// before
modes:
  - id: "admin"
    ...
  - id: "admin"
    ...
// after
modes:
  - id: "admin"
    ...
  - id: "admin-backup"
    ...
Defensive patterns

Strategy: validation

Validate before calling

use std::collections::HashSet;
let mut ids = HashSet::new();
for mode in &auth_config.modes {
    if !ids.insert(mode.identifier.clone()) {
        return Err(format!("duplicate alternative mode identifier: {}", mode.identifier));
    }
}

Type guard

fn modeIdsUnique(modes: &[Mode]) -> bool {
    let mut s = std::collections::HashSet::new();
    modes.iter().all(|m| s.insert(m.identifier.clone()))
}

Prevention

When it happens

Trigger: 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).

Common situations: 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".

Related errors


AI-assisted analysis of hasura/graphql-engine@724551b9ae (2026-08-28). Data as JSON: /api/errors/332b40cf9e177007. Report an issue: GitHub.