facebook/relay · error
BabelPluginRelay: Substitutions are not allowed in graphql f
Error message
BabelPluginRelay: Substitutions are not allowed in graphql fragments. Included fragments should be referenced as `...MyModule_propName`.
What it means
Relay's graphql`` tags must be pure string literals: the template's quasis must be exactly one chunk with zero ${} substitutions. Dynamic interpolation would make the GraphQL document unknowable at build time, so the plugin throws. Included fragments should be referenced statically with `...MyModule_propName` spread syntax instead.
Source
Thrown at packages/babel-plugin-relay/getValidGraphQLTag.js:33
const GraphQL = require('graphql');
/**
* Given a babel AST path to a tagged template literal, return an AST if it is
* a graphql literal being used in a valid way.
* If it is some other type of template literal then return nothing.
*/
function getValidGraphQLTag(path: any): ?DocumentNode {
const tag = path.get('tag');
if (!tag.isIdentifier({name: 'graphql'})) {
return null;
}
const quasis = path.node.quasi.quasis;
if (quasis.length !== 1) {
throw new Error(
'BabelPluginRelay: Substitutions are not allowed in graphql fragments. ' +
'Included fragments should be referenced as `...MyModule_propName`.',
);
}
const text = quasis[0].value.raw;
const ast = GraphQL.parse(text, {experimentalFragmentVariables: true});
if (ast.definitions.length === 0) {
throw new Error('BabelPluginRelay: Unexpected empty graphql tag.');
}
return ast;
}
module.exports = getValidGraphQLTag;
View on GitHub (pinned to 668b1b85e0)
Solutions
- Remove all ${...} interpolations and write the GraphQL text statically
- Replace dynamically injected sub-selections with fragment spreads: `...SomeModule_propName`
- If field reuse is needed, define a named fragment once and spread it into the consuming fragment
Example fix
// before
const fields = 'id name';
const frag = graphql`fragment F on User { ${fields} }`;
// after
const frag = graphql`
fragment F on User {
id
name
}
`; Defensive patterns
Strategy: validation
Validate before calling
function assertNoSubstitutions(taggedTemplateNode) {
if (taggedTemplateNode.quasi.expressions.length > 0 || taggedTemplateNode.quasi.quasis.length !== 1) {
throw new Error('graphql`` tags must not contain ${} substitutions');
}
} Type guard
const isStaticTemplate = (node) => node && node.quasi && node.quasi.expressions.length === 0 && node.quasi.quasis.length === 1;
Prevention
- Never interpolate JavaScript variables into graphql`` tags
- Reuse fields with named fragment spreads (...SomeModule_propName) instead of templating
- Enable eslint-plugin-relay rules to flag dynamic graphql templates
When it happens
Trigger: A graphql`` template containing an interpolation, e.g. graphql`fragment F on User ${someField} {...}` or graphql`query { ${variable} }`, making template.quasi.quasis.length > 1.
Common situations: Trying to build queries dynamically from variables; generating field lists at runtime; templating shared field sets via JS variables instead of fragment spreads.
Related errors
- BabelPluginRelay: Expected exactly one definition per graphq
- BabelPluginRelay: Expected a fragment, mutation, query, or s
- GraphQL operations and fragments must contain names
- BabelPluginRelay: Unexpected empty graphql tag.
- BabelPluginRelay: Expected plugin context to include "types"
AI-assisted analysis of facebook/relay@668b1b85e0 (2026-09-02).
Data as JSON: /api/errors/88e88f4bb10f9a28.
Report an issue: GitHub.