facebook/relay · error
fetchQuery: Invalid fetchPolicy ${fetchPolicy}
Error message
fetchQuery: Invalid fetchPolicy ${fetchPolicy} What it means
fetchQuery validates the fetchPolicy argument against a switch of known policies ('network-only', 'store-or-network', etc.). The default case is reached when the caller passes an unrecognized or misspelled policy, so Relay throws instead of silently falling back to a default.
Source
Thrown at packages/relay-runtime/query/fetchQuery.js:178
).map(readData);
} else {
observable = getNetworkObservable<$FlowFixMe>(
environment,
operation,
).map(readData);
}
environment.__log({
name: 'fetchquery.fetch',
operation,
fetchPolicy,
queryAvailability,
shouldFetch,
});
return observable;
}
default:
fetchPolicy as empty;
throw new Error('fetchQuery: Invalid fetchPolicy ' + fetchPolicy);
}
}
function getNetworkObservable<TQuery extends OperationType>(
environment: IEnvironment,
operation: OperationDescriptor,
): RelayObservable<TQuery['response']> {
return fetchQueryInternal
.fetchQuery(environment, operation)
.map(() => environment.lookup(operation.fragment));
}
module.exports = fetchQuery;
View on GitHub (pinned to 668b1b85e0)
Solutions
- Use one of the supported policies: 'store-only' | 'store-or-network' | 'store-and-network' | 'network-only'
- Import the FetchPolicy type from relay-runtime and annotate the option so typos are caught at compile time
- If porting from Apollo, map the old policy to the closest Relay equivalent (e.g. network-first -> store-or-network with network-only for forced refresh)
- Check the installed relay version's fetchQuery.js switch to confirm the policy still exists
Example fix
// before
fetchQuery(env, query, vars, {fetchPolicy: 'cache-first'});
// after
fetchQuery(env, query, vars, {fetchPolicy: 'store-or-network'}); Defensive patterns
Strategy: validation
Validate before calling
const VALID = ['store-only','store-or-network','store-and-network','network-only'];
if (fetchPolicy != null && !VALID.includes(fetchPolicy)) throw new TypeError(`Invalid fetchPolicy: ${fetchPolicy}`);
fetchQuery(env, query, vars, {fetchPolicy}); Type guard
const isFetchPolicy = (p) => ['store-only','store-or-network','store-and-network','network-only'].includes(p);
Try / catch
try {
fetchQuery(env, q, vars, {fetchPolicy});
} catch (e) {
if (String(e.message).startsWith('fetchQuery: Invalid fetchPolicy')) {
console.error('Bad fetchPolicy, falling back to store-or-network');
} else throw e;
} Prevention
- Annotate options with the FetchPolicy type so typos fail at compile time
- Never hand-write policy strings; use constants or literals checked by TS
- Don't import Apollo policy names into Relay code
- Re-check policies after Relay upgrades
When it happens
Trigger: Calling environment.fetchQuery(query, variables, {fetchPolicy: '...'}) or environment.executeWithFetchPolicy with a policy string other than store-only, store-or-network, store-and-network, or network-only — e.g. a typo like 'network_first' or a policy copied from a different library like 'cache-first'.
Common situations: Copy-pasting Apollo Client fetch policies (cache-first, network-first) into Relay code; hand-rolled string variables typed loosely; version drift where an experimental policy was removed or renamed.
Understand the failure class
Background: Invalid enum value errors: "Unknown type", "Invalid scope", "must be one of" — when a string is not on the library's allowed list — this error's family across 23 libraries.
Related errors
- BabelPluginRelay: Expected plugin context to include "types"
- BabelPluginRelay: Expected exactly one definition per graphq
- BabelPluginRelay: Expected a fragment, mutation, query, or s
- GraphQL operations and fragments must contain names
- Babel state is missing expected file name
AI-assisted analysis of facebook/relay@668b1b85e0 (2026-09-02).
Data as JSON: /api/errors/84522eda888f699a.
Report an issue: GitHub.