facebook/relay · error

Relay: Resolver error at path '${fieldError.fieldPath}' in '

Error message

Relay: Resolver error at path '${fieldError.fieldPath}' in '${fieldError.owner}'. Message: ${fieldError.error.message}

What it means

handlePotentialSnapshotErrors converts field-level error events recorded during a store lookup into thrown errors. When a 'relay_resolver.error' event is marked to throw (eventShouldThrow), it throws an Error embedding the resolver field path, owner operation, and the underlying error message, surfacing resolver failures to the caller of the snapshot read.

Source

Thrown at packages/relay-runtime/util/handlePotentialSnapshotErrors.js:48

    // site's error handling infrastructure.

    // Awkward. We don't want to attach the ui context in RelayReader where we
    // create the event, but it means we need to add it here instead of just
    // passing the event through.

    environment.relayFieldLogger({
      // the uiContext on fieldError undefined *always*,
      ...fieldError,
      // and this is where we assign loggingContext to uiContext to populate it
      uiContext: loggingContext,
    });
  }

  for (const fieldError of fieldErrors) {
    if (eventShouldThrow(fieldError)) {
      switch (fieldError.kind) {
        case 'relay_resolver.error':
          throw new Error(
            `Relay: Resolver error at path '${fieldError.fieldPath}' in '${fieldError.owner}'. Message: ${fieldError.error.message}`,
          );
        case 'relay_field_payload.error':
          throw new Error(
            `Relay: Received a field error in the server response for field '${fieldError.fieldPath}' in '${fieldError.owner}'. Message: ${fieldError.error.message}`,
          );
        case 'missing_expected_data.throw':
          throw new Error(
            `Relay: Missing expected data at path '${fieldError.fieldPath}' in '${fieldError.owner}'. See https://relay.dev/docs/next/debugging/why-null/ for likely causes.`,
          );
        case 'missing_required_field.throw': {
          let reason: string;
          if (fieldError.fieldValue === null) {
            reason =
              fieldError.fieldError != null
                ? `the server returned null with an error: ${fieldError.fieldError.message}`
                : 'the server returned null';
          } else {

View on GitHub (pinned to 668b1b85e0)

Solutions

  1. Inspect fieldPath/owner in the message to locate the failing resolver and fix its logic (null checks, expected data)
  2. Wrap the resolver's data access with @required or handle missing data so it degrades instead of throwing
  3. Catch the error at the query boundary (ErrorBoundary / try-catch around lookup) and render an error UI
  4. If the error should be logged not thrown, change disposition (remove @throwOnFieldError or configure relayFieldLogger handling)

Example fix

// before
// resolver throws on missing actor
const name = actor.name;
// after
const name = actor?.name ?? 'unknown';
Defensive patterns

Strategy: try-catch

Validate before calling

null

Type guard

null

Try / catch

try {
  const snapshot = environment.lookup(fragment);
  handlePotentialSnapshotErrors(env, snapshot.fieldErrors, snapshot.missingClientEdges);
  return snapshot;
} catch (e) {
  if (String(e.message).startsWith('Relay: Resolver error at path')) {
    renderResolverErrorUI(e);
    return null;
  }
  throw e;
}

Prevention

When it happens

Trigger: Reading a snapshot (e.g. via environment.lookup or useLazyLoadQuery render) where a Live Resolver threw during evaluation, and the error's disposition is 'throw' rather than log.

Common situations: A Resolver function throws or rejects during query/render; @throwOnFieldError directive present; tests asserting on resolver errors; missing required data inside a resolver causing it to raise.

Related errors


AI-assisted analysis of facebook/relay@668b1b85e0 (2026-09-02). Data as JSON: /api/errors/4fd953d53929b8ee. Report an issue: GitHub.