facebook/relay · error

Expect fragment to exist.

Error message

Expect fragment to exist.

What it means

The root_variables transform resolves each fragment spread by looking the fragment definition up in the program and expects it to exist ("Expect fragment to exist."). It needs the definition to read @arguments variable definitions and type-check root variables passed to the spread. A missing definition means the spread references a fragment that was never added to the compiled program.

Source

Thrown at compiler/crates/relay-transforms/src/root_variables.rs:268

            && let Some(relay_resolver_metadata) = RelayResolverMetadata::from(directive)
        {
            for arg in relay_resolver_metadata.field_arguments.iter() {
                if let Value::Variable(var) = &arg.value.item
                    && self.is_root_variable(var.name.item)
                {
                    self.record_root_variable_usage(&var.name, &var.type_);
                }
            }
        }
        self.default_visit_directive(directive);
    }

    fn visit_fragment_spread(&mut self, spread: &FragmentSpread) {
        self.visit_directives(&spread.directives);
        let fragment = self
            .program
            .fragment(spread.fragment.item)
            .expect("Expect fragment to exist.");

        // Detect root variables being passed as the value of @arguments;
        // recover the expected type from the corresponding argument definitions.
        if !fragment.variable_definitions.is_empty() {
            for arg in spread.arguments.iter() {
                if let Value::Variable(var) = &arg.value.item
                    && let Some(def) = fragment
                        .variable_definitions
                        .named(VariableName(arg.name.item.0))
                    && self.is_root_variable(var.name.item)
                {
                    self.record_root_variable_usage(&var.name, &def.type_);
                }
            }
        }

        // Merge any root variables referenced by the spread fragment
        // into this (parent) fragment's arguments.

View on GitHub (pinned to 668b1b85e0)

Solutions

  1. Compile the entire program (all documents) together so every spread's fragment is present.
  2. Check your relay compiler config (include/exclude globs, docs/extension) so the fragment's file is picked up.
  3. Verify earlier validation passes (undefined fragment detection) run before root_variables.
  4. If the fragment was intentionally removed, remove the stale spread from the operation.

Example fix

// relay.config.js — before
include: ['src/queries/**'],
// after
include: ['src/**'],  // ensure fragments referenced by queries are compiled
Defensive patterns

Strategy: validation

Validate before calling

if program.fragment(spread.fragment.item).is_none() {
    return Err(vec![Diagnostic::error(format!("Undefined fragment '{}'", spread.fragment.item))]));
}

Type guard

fn spread_resolvable(program: &Program, spread: &FragmentSpread) -> bool { program.fragment(spread.fragment.item).is_some() }

Try / catch

let Some(fragment) = program.fragment(spread.fragment.item) else { continue; };

Prevention

When it happens

Trigger: A fragment spread whose target fragment is absent from the program — compiling per-file so the spread's fragment lives in an uncompiled module, a typo'd fragment name that skipped earlier validation, or a transform order where fragments were pruned before root_variables runs.

Common situations: Per-document compilation instead of whole-program compilation; fragments declared in files excluded from the compiler's document list; monorepo setups where the include globs miss the fragment's module.

Related errors


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