hasura/graphql-engine · error · DuplicateRootFieldError
Cannot add query root field {0} as it already in use
Error message
Cannot add query root field {0} as it already in use What it means
DuplicateRootFieldError::Query from the TrackGraphQLRootFields helper in metadata-resolve. It fires when two metadata components try to register a GraphQL root field with the same name on the Query root type (e.g. two commands or a command and a model with identical GraphQL names).
Source
Thrown at v3/crates/metadata-resolve/src/helpers/types.rs:36
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, Hash)]
pub struct NdcColumnForComparison {
pub column: DataConnectorColumnName,
pub equal_operator: DataConnectorOperatorName,
}
/// Track the root fields of the GraphQL schema while resolving the metadata.
/// This is used to ensure that the schema has unique root fields for Query, Mutation and Subscription.
// NOTE: The `ast::Name` is cheap to clone, so storing them directly without references
pub struct TrackGraphQLRootFields {
pub query: BTreeSet<ast::Name>,
pub mutation: BTreeSet<ast::Name>,
pub subscription: BTreeSet<ast::Name>,
}
#[derive(Debug, thiserror::Error)]
pub enum DuplicateRootFieldError {
#[error("Cannot add query root field {0} as it already in use")]
Query(ast::Name),
#[error("Cannot add mutation root field {0} as it already in use")]
Mutation(ast::Name),
#[error("Cannot add subscription root field {0} as it already in use")]
Subscription(ast::Name),
}
impl TrackGraphQLRootFields {
pub fn new() -> Self {
Self {
query: BTreeSet::new(),
mutation: BTreeSet::new(),
subscription: BTreeSet::new(),
}
}
pub fn track_query_root_field(
&mut self,
name: &ast::Name,View on GitHub (pinned to 724551b9ae)
Solutions
- Rename one of the colliding GraphQL root fields (set a distinct graphql field name on the command/model)
- Delete the duplicated metadata object if it was accidentally cloned
- If a model plus custom command overlap, expose the command on the model rather than at the root or vice versa
Example fix
# before - kind: Command name: users # ... - kind: Model name: users # same root field name # after - kind: Command name: users_custom # renamed to avoid collision
Defensive patterns
Strategy: validation
Validate before calling
// Before adding, check uniqueness (mirrors the library's own BTreeSet check):
let inserted = query_fields.insert(name.clone());
if !inserted {
return Err(DuplicateRootFieldError::Query(name));
} Try / catch
match err {
DuplicateRootFieldError::Query(name) => eprintln!("rename the command/model exposing query field '{name}'"),
_ => {}
} Prevention
- Adopt per-domain naming conventions for root fields
- Run a name-collision lint over all models/commands in CI
- Rename rather than delete-and-recreate when refactoring
When it happens
Trigger: Calling the query root-field registration API twice with the same ast::Name, e.g. a model and a derived command both named users_by_pk exposed at the Query root.
Common situations: Copy-pasted commands with the same GraphQL field name, a command name colliding with a model root field, or two relationships/commands in different subgraphs producing the same root field name.
Related errors
- Cannot add mutation root field {0} as it already in use
- Cannot add subscription root field {0} as it already in use
- the boolean expression type with name {type_name} is defined
- the following command is defined more than once: {name:}
- only one enumTypeNames can be defined in GraphqlConfig, whos
AI-assisted analysis of hasura/graphql-engine@724551b9ae (2026-08-28).
Data as JSON: /api/errors/868c217a9d8b97c7.
Report an issue: GitHub.