hasura/graphql-engine · error · RelayError

Found multiple models {model_1:}, {model_2:} that implement

Error message

Found multiple models  {model_1:}, {model_2:} that implement the same object type {object_type:} to be global ID sources.

What it means

Relay stage error: two different models (both named in the message) that implement the same object type are marked globalIdSource: true. Relay requires a unique global ID source per object type; the resolver cannot pick between them and aborts. Note the doubled space in the message is cosmetic.

Source

Thrown at v3/crates/metadata-resolve/src/stages/relay/mod.rs:39

    Ok(())
}

#[derive(Debug, thiserror::Error)]
pub enum RelayError {
    #[error(
        "'globalIdFields' for type {object_type:} found, but no model found with 'globalIdSource: true' for type {object_type:}"
    )]
    GlobalIdSourceNotDefined {
        object_type: Qualified<CustomTypeName>,
    },
    #[error(
        "Model {model_name:} is marked as a global ID source but there are no global id fields present in the related object type {type_name:}"
    )]
    NoGlobalFieldsPresentInGlobalIdSource {
        type_name: Qualified<CustomTypeName>,
        model_name: ModelName,
    },
    #[error(
        "Found multiple models  {model_1:}, {model_2:} that implement the same object type {object_type:} to be global ID sources."
    )]
    DuplicateModelGlobalIdSource {
        model_1: Qualified<ModelName>,
        model_2: Qualified<ModelName>,
        object_type: Qualified<CustomTypeName>,
    },
    #[error("model {model_name:} with arguments is unsupported as a global ID source")]
    ModelWithArgumentsAsGlobalIdSource { model_name: Qualified<ModelName> },
}

View on GitHub (pinned to 724551b9ae)

Solutions

  1. Pick one model to be the global ID source and remove globalIdSource: true from the other
  2. If both models are accidental duplicates, delete one entirely
  3. If types are meant to be distinct, fix the objectType mapping so they no longer collide

Example fix

# before
models:
  - name: Users
    objectType: User
    globalIdSource: true
  - name: UsersV2
    objectType: User
    globalIdSource: true
# after
models:
  - name: Users
    objectType: User
    globalIdSource: true
  - name: UsersV2
    objectType: User
Defensive patterns

Strategy: validation

Validate before calling

fn check_single_global_id_source(models: &[Model]) -> Result<(), String> {
    let mut seen: HashMap<_, Vec<_>> = HashMap::new();
    for m in models {
        if m.global_id_source { seen.entry(m.object_type.clone()).or_default().push(m.name.clone()); }
    }
    for (ty, names) in seen {
        if names.len() > 1 { return Err(format!("multiple globalIdSource models for {ty}: {names:?}")); }
    }
    Ok(())
}

Try / catch

catch DuplicateModelGlobalIdSource and print both model qualified names plus the shared object type

Prevention

When it happens

Trigger: Two models with globalIdSource: true whose objectType mappings resolve to the same Qualified<CustomTypeName> — often the same model defined in two subgraphs/namespaces, or a duplicated model config.

Common situations: Copying a Relay-enabled model into another namespace without clearing the flag; consolidating models onto one type while both keep globalIdSource; federation-style setups where the same type is implemented per-subgraph.

Related errors


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