facebook/relay · error

has_root_fragment() returned true

Error message

has_root_fragment() returned true

What it means

During resolver metadata validation, Relay's shadow transform expects that a field marked as having a root fragment (per has_root_fragment()) always carries a fragment_name in its metadata. The expect unwraps that Option; if absent, an earlier validation pass and the has_root_fragment computation disagreed. This is an internal invariant check, so hitting it indicates corrupted or inconsistently built resolver field metadata.

Source

Thrown at compiler/crates/relay-transforms/src/relay_resolvers/shadow_transform.rs:277

            self.errors.push(Diagnostic::error(
                ValidationMessage::ReturnFragmentInvalidModuleName {
                    module_name,
                    fragment_name: return_fragment.item.0,
                },
                location,
            ));
        }

        // Validate: return fragment must be spread within the root fragment.
        //
        // The `@returnFragment` placeholder spread is converted into the
        // schema-known internal `@__relay_shadow_return` directive at the AST
        // rail (before `build_ir`). So we look for that marker directive (keyed
        // on the return fragment name), not the original spread, which no longer
        // exists by the time we reach IR.
        let root_fragment_name = field_metadata
            .fragment_name
            .expect("has_root_fragment() returned true");

        if let Some(root_fragment) = self.program.fragment(root_fragment_name)
            && !selections_contain_shadow_return_marker(
                &root_fragment.selections,
                return_fragment.item,
            )
        {
            self.errors.push(Diagnostic::error(
                ValidationMessage::ReturnFragmentNotSpreadInRootFragment {
                    return_fragment_name: return_fragment.item,
                    root_fragment_name,
                },
                location,
            ));
        }
    }
}

View on GitHub (pinned to 668b1b85e0)

Solutions

  1. Ensure the @relay_resolver directive includes fragment_name (and required arguments) so has_root_fragment and fragment_name agree.
  2. Re-run the full compiler pipeline from source rather than incremental artifacts so metadata is rebuilt consistently.
  3. Check custom transforms that construct RelayResolverFieldMetadata and make them set fragment_name whenever the root-fragment flag is true.
  4. File an issue with the failing document if the directive looks correct — it indicates an internal invariant bug.

Example fix

// before
@relay_resolver(import_path: "ImageResolver") // no fragment_name
// after
@relay_resolver(import_path: "ImageResolver", fragment_name: "ImageResolverFragment", root_fragment_name: "ImageResolverRootFragment")
Defensive patterns

Strategy: validation

Validate before calling

// Pre-check resolver directive metadata coherence before compiling:
if directive.args.get("has_root_fragment") == Some(&Bool(true)) {
    assert!(directive.args.contains_key("fragment_name"), "@relay_resolver with root fragment requires fragment_name");
}

Type guard

fn metadata_is_coherent(meta: &RelayResolverFieldMetadata) -> bool {
    !(meta.has_root_fragment() && meta.fragment_name.is_none())
}

Try / catch

// Panics are not catchable as errors; pre-validate:
let meta = RelayResolverFieldMetadata::find(directives).ok_or("not a resolver field")?;
if !metadata_is_coherent(&meta) {
    return Err(anyhow!("resolver metadata: has_root_fragment set without fragment_name"));
}

Prevention

When it happens

Trigger: validate_resolver_metadata processes a field whose RelayResolverFieldMetadata has has_root_fragment()==true but fragment_name==None — e.g. the @relay_resolver directive lacked fragment_name/root-fragment arguments, or custom metadata construction set the flag without the name.

Common situations: Hand-written or partially generated @relay_resolver directives missing the fragment_name argument; a mis-ordered transform pipeline running shadow_transform before metadata validation populates fragment_name; upgrading Relay with older persisted schema metadata.

Related errors


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