facebook/relay · error
commitMutation: Expected mutation operation
Error message
commitMutation: Expected mutation operation
What it means
applyOptimisticMutation's commitMutation validates that the compiled operation passed in config.mutation has operationKind === 'mutation'. This error means a non-mutation operation (query, subscription, or a plural/abstract operation) was supplied to an optimistic mutation commit.
Source
Thrown at packages/relay-runtime/mutations/applyOptimisticMutation.js:54
optimisticResponse?: Object,
};
/**
* Higher-level helper function to execute a mutation against a specific
* environment.
*/
function applyOptimisticMutation<TMutation extends MutationParameters>(
environment: IEnvironment,
config: OptimisticMutationConfig<TMutation>,
): Disposable {
invariant(
isRelayModernEnvironment(environment),
'commitMutation: expected `environment` to be an instance of ' +
'`RelayModernEnvironment`.',
);
const mutation = getRequest(config.mutation);
if (mutation.params.operationKind !== 'mutation') {
throw new Error('commitMutation: Expected mutation operation');
}
let {optimisticUpdater} = config;
const {configs, optimisticResponse, variables} = config;
const operation = createOperationDescriptor(mutation, variables);
if (configs) {
({optimisticUpdater} = RelayDeclarativeMutationConfig.convert(
configs,
mutation,
optimisticUpdater,
));
}
return environment.applyMutation({
operation,
response: optimisticResponse,
updater: optimisticUpdater,
});
}View on GitHub (pinned to 668b1b85e0)
Solutions
- Ensure the .graphql file declares `mutation MutationName { ... }` and re-run relay-compiler to regenerate artifacts
- Check that the imported module in config.mutation is the intended mutation's generated artifact, not a query's
- If you intended a subscription, use requestSubscription; if a query, use fetchQuery/usePreloadedQuery instead
Example fix
// before (Mutation.graphql)
query UpsertUserMutation($input: X!) { ... }
// after
mutation UpsertUserMutation($input: X!) { ... } Defensive patterns
Strategy: validation
Validate before calling
import assert from 'assert';
import {getRequest} from 'relay-runtime';
const mutation = getRequest(config.mutation);
assert.strictEqual(mutation.params.operationKind, 'mutation', 'config.mutation must be a compiled mutation'); Type guard
function isMutationRequest(req: {params: {operationKind: string}}): boolean {
return req.params.operationKind === 'mutation';
} Try / catch
try {
commitMutation(environment, config);
} catch (e) {
if (e.message.includes('Expected mutation operation')) {
console.error('Wrong artifact passed to commitMutation:', config.mutation.params.name);
}
} Prevention
- Type mutation configs with the generated $Parameters types so wrong artifacts fail at compile time
- Name mutation GraphQL operations with a Mutation suffix and match import names
- Recompile after editing operation keywords
- Add a unit test asserting operationKind for every mutation config used in the app
When it happens
Trigger: Calling commitMutation(environment, {mutation: SomeQuery}) where the GraphQL document was compiled as a query/subscription; or passing the wrong generated artifact ($Parameters import) to the optimistic mutation API.
Common situations: Importing the wrong generated file after editing the GraphQL document and changing its operation keyword; copying a query request into a mutation call; codegen mismatches where the .graphql file says 'query' but the JS treats it as a mutation.
Related errors
- commitMutation: Expected mutation operation
- commitMutation: Expected mutation to be of type request
- unexpected value for @defer if argument: {other:?}
- unexpected value for @stream if argument: {other:?}
- Expected client edge backing field to be transformed into ex
AI-assisted analysis of facebook/relay@668b1b85e0 (2026-09-02).
Data as JSON: /api/errors/37317cf153b04c29.
Report an issue: GitHub.