facebook/relay · error
fetchQuery: Expected query operation
Error message
fetchQuery: Expected query operation
What it means
fetchQuery_DEPRECATED only supports GraphQL queries: it executes an operation and resolves with its data. The throw guards against passing a mutation/subscription fragment, whose execution semantics differ, so Relay rejects it up front.
Source
Thrown at packages/relay-runtime/query/fetchQuery_DEPRECATED.js:37
createOperationDescriptor,
} = require('../store/RelayModernOperationDescriptor');
const {getRequest} = require('./GraphQLTag');
/**
* A helper function to fetch the results of a query. Note that results for
* fragment spreads are masked: fields must be explicitly listed in the query in
* order to be accessible in the result object.
*/
function fetchQuery_DEPRECATED<T extends OperationType>(
environment: IEnvironment,
taggedNode: GraphQLTaggedNode,
variables: T['variables'],
cacheConfig?: ?CacheConfig,
): Promise<T['response']> {
const query = getRequest(taggedNode);
if (query.params.operationKind !== 'query') {
throw new Error('fetchQuery: Expected query operation');
}
const operation = createOperationDescriptor(query, variables, cacheConfig);
return environment
.execute({operation})
.map(() => environment.lookup(operation.fragment).data)
.toPromise();
}
module.exports = fetchQuery_DEPRECATED;
View on GitHub (pinned to 668b1b85e0)
Solutions
- Pass a query operation (use graphql`query ... {...}`) to fetchQuery
- Use commitMutation (relay-experimental/commitMutation or environment) for mutations instead of fetchQuery
- Use requestSubscription for subscriptions
- Migrate off fetchQuery_DEPRECATED to fetchQuery, which produces a clearer pathway
Example fix
// before
fetchQuery_DEPRECATED(env, graphql`mutation M { ... }`, vars);
// after
commitMutation(env, {mutation: graphql`mutation M { ... }`, variables: vars}); Defensive patterns
Strategy: validation
Validate before calling
const req = getRequest(taggedNode);
if (req.params.operationKind !== 'query') {
throw new TypeError('fetchQuery requires a query operation, got ' + req.params.operationKind);
} Type guard
const isQueryOp = (tag) => getRequest(tag).params.operationKind === 'query';
Try / catch
try {
return await fetchQuery_DEPRECATED(env, tag, vars);
} catch (e) {
if (e.message === 'fetchQuery: Expected query operation') {
throw new Error('Pass queries to fetchQuery; use commitMutation/requestSubscription for other kinds');
}
throw e;
} Prevention
- Keep queries and mutations in separate modules/exports
- Use relay-compiler typed graphql so operation kinds are typed
- Prefer modern fetchQuery/commitMutation APIs
- Name tagged constants by kind (e.g. xxxQuery, xxxMutation)
When it happens
Trigger: Passing a graphql-tagged node whose params.operationKind is 'mutation' or 'subscription' to the deprecated fetchQuery(environment, taggedNode, variables) helper.
Common situations: Using a shared fragment/mutation constant by mistake in a preloading helper; refactoring code where the tag changed kind; forgetting that commitMutation/requestSubscription are the right entry points for non-query operations.
Related errors
- requestSubscription: Must use Subscription operation
- BabelPluginRelay: Expected exactly one definition per graphq
- BabelPluginRelay: Expected a fragment, mutation, query, or s
- GraphQL operations and fragments must contain names
- BabelPluginRelay: Substitutions are not allowed in graphql f
AI-assisted analysis of facebook/relay@668b1b85e0 (2026-09-02).
Data as JSON: /api/errors/e123618a652c82f0.
Report an issue: GitHub.