facebook/relay · error
Unexpected null response from fetchQuery
Error message
Unexpected null response from fetchQuery
What it means
serverFetchQuery is an RSC (React Server Components) wrapper around Relay's fetchQuery. It awaits the observable's promise and throws if Relay resolves with null/undefined, which should never happen for a well-formed query — a null result indicates the environment or network layer returned an empty payload instead of data or an error.
Source
Thrown at packages/react-relay/relay-hooks/rsc/serverFetchQuery.js:26
* @format
* @oncall relay
*/
'use strict';
import type {IEnvironment, Query, Variables} from 'relay-runtime';
const {fetchQuery} = require('relay-runtime');
async function serverFetchQuery<TVariables extends Variables, TData>(
environment: IEnvironment,
query: Query<TVariables, TData>,
variables: TVariables,
): Promise<TData> {
const observable = fetchQuery(environment, query, variables);
const result = await observable.toPromise();
if (result == null) {
throw new Error('Unexpected null response from fetchQuery');
}
return result;
}
module.exports = serverFetchQuery;
View on GitHub (pinned to 668b1b85e0)
Solutions
- Inspect the network layer's fetch function and make sure it always resolves with a valid GraphQLResponse containing a 'data' or 'errors' field
- Check that the fetch response body is being parsed (e.g. await response.json()) and that the server actually returns a body for this query
- Verify the environment passed to serverFetchQuery is a fully configured RelayModernEnvironment, not an empty/placeholder environment
- If the error is expected (e.g. legitimately nullable data), handle the null in the network layer or wrap the call and surface a domain-specific error
Example fix
// before
const network = Network.create(() => fetch(url));
// after
const network = Network.create(async (params, vars) => {
const res = await fetch(url, {method: 'POST', body: JSON.stringify({query: params.text, variables: vars})});
if (!res.ok) throw new Error(`GraphQL request failed: ${res.status}`);
return res.json(); // must resolve with {data} or {errors}
}); Defensive patterns
Strategy: try-catch
Validate before calling
const network = environment.getNetwork(); // verify fetch always resolves {data} or {errors}
// sanity check before use:
function isValidResponse(r) { return r != null && ('data' in r || 'errors' in r); } Type guard
function isGraphQLResponse(r: unknown): r is {data: unknown} { return typeof r === 'object' && r !== null && 'data' in r; } Try / catch
try {
const data = await serverFetchQuery(environment, query, variables);
} catch (e) {
if (e.message.includes('Unexpected null response')) {
// network layer returned null: log request id/params and surface a server error
}
throw e;
} Prevention
- Always parse and validate the fetch response body in your network layer
- Never let a network layer resolve undefined; throw on !res.ok
- Test your RSC network layer with an integration test per query
- Log the query name/variables when this error fires to identify the failing environment
When it happens
Trigger: Calling serverFetchQuery(environment, query, variables) in a server environment when the network layer's fetch resolves with null/undefined (e.g. missing response body, custom network layer returning undefined, or environment misconfiguration) instead of a valid GraphQLResponse.
Common situations: RSC server-side rendering with a misconfigured network layer; a fetch function that returns response.json() of an empty body (204/empty 200); a network layer that swallows errors and resolves undefined; SSR data prefetch where the environment has no handler for the query.
AI-assisted analysis of facebook/relay@668b1b85e0 (2026-09-02).
Data as JSON: /api/errors/60e9352d233a621c.
Report an issue: GitHub.