{"record":{"id":"868c217a9d8b97c7","repo":"hasura/graphql-engine","slug":"cannot-add-query-root-field-0-as-it-already-in-u","errorCode":null,"errorMessage":"Cannot add query root field {0} as it already in use","messagePattern":"Cannot add query root field (.+?) as it already in use","errorType":"validation","errorClass":"DuplicateRootFieldError","httpStatus":null,"severity":"error","filePath":"v3/crates/metadata-resolve/src/helpers/types.rs","lineNumber":36,"sourceCode":"\n#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, Hash)]\npub struct NdcColumnForComparison {\n    pub column: DataConnectorColumnName,\n    pub equal_operator: DataConnectorOperatorName,\n}\n\n/// Track the root fields of the GraphQL schema while resolving the metadata.\n/// This is used to ensure that the schema has unique root fields for Query, Mutation and Subscription.\n// NOTE: The `ast::Name` is cheap to clone, so storing them directly without references\npub struct TrackGraphQLRootFields {\n    pub query: BTreeSet<ast::Name>,\n    pub mutation: BTreeSet<ast::Name>,\n    pub subscription: BTreeSet<ast::Name>,\n}\n\n#[derive(Debug, thiserror::Error)]\npub enum DuplicateRootFieldError {\n    #[error(\"Cannot add query root field {0} as it already in use\")]\n    Query(ast::Name),\n    #[error(\"Cannot add mutation root field {0} as it already in use\")]\n    Mutation(ast::Name),\n    #[error(\"Cannot add subscription root field {0} as it already in use\")]\n    Subscription(ast::Name),\n}\n\nimpl TrackGraphQLRootFields {\n    pub fn new() -> Self {\n        Self {\n            query: BTreeSet::new(),\n            mutation: BTreeSet::new(),\n            subscription: BTreeSet::new(),\n        }\n    }\n    pub fn track_query_root_field(\n        &mut self,\n        name: &ast::Name,","sourceCodeStart":18,"sourceCodeEnd":54,"githubUrl":"https://github.com/hasura/graphql-engine/blob/724551b9ae87845594ef0408cff0e50eb6c90dc5/v3/crates/metadata-resolve/src/helpers/types.rs#L18-L54","documentation":"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).","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"# before\n- kind: Command\n  name: users\n# ...\n- kind: Model\n  name: users   # same root field name\n\n# after\n- kind: Command\n  name: users_custom   # renamed to avoid collision","handlingStrategy":"validation","validationCode":"// Before adding, check uniqueness (mirrors the library's own BTreeSet check):\nlet inserted = query_fields.insert(name.clone());\nif !inserted {\n    return Err(DuplicateRootFieldError::Query(name));\n}","typeGuard":null,"tryCatchPattern":"match err {\n    DuplicateRootFieldError::Query(name) => eprintln!(\"rename the command/model exposing query field '{name}'\"),\n    _ => {}\n}","preventionTips":["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"],"tags":["graphql","metadata","naming","duplicate"],"backgroundTag":"duplicate-graphql-field","analyzedSha":"724551b9ae87845594ef0408cff0e50eb6c90dc5","analyzedAt":"2026-08-28T07:32:55.105Z","schemaVersion":2},"datasetVersion":"2026-08-28T11:17:15.048Z"}