facebook/relay · error

Expect the module import inline fragment to have a type

Error message

Expect the module import inline fragment to have a type

What it means

split_module_import's transform_inline_fragment only processes inline fragments identified as module imports; after confirming it is one, it force-unwraps the fragment's type_condition. The expect fires when a module-import inline fragment has no type condition (e.g. ... @module { ... } with no 'on Type'), since the split/normalization operation requires it.

Source

Thrown at compiler/crates/relay-transforms/src/match_/split_module_import.rs:114

        }
    }

    fn transform_inline_fragment(&mut self, fragment: &InlineFragment) -> Transformed<Selection> {
        if let Some(module_metadata) = self.inline_module_metadata(fragment) {
            // We do not need to write normalization files for base fragments.
            // This is because when we process the base project, the normalization fragment will
            // be written, and we do not want to emit multiple normalization fragments with
            // the same name. If we did, Haste would complain about a duplicate module definition.
            if self
                .base_fragment_names
                .contains(&module_metadata.fragment_name)
            {
                return self.default_transform_inline_fragment(fragment);
            }

            let parent_type = fragment
                .type_condition
                .expect("Expect the module import inline fragment to have a type");

            let normalization_name =
                get_normalization_operation_name(module_metadata.fragment_name.0).intern();
            let schema = &self.program.schema;
            let created_split_operation = self
                .split_operations
                .entry(normalization_name)
                .or_insert_with(|| {
                    // Exclude `__module_operation/__module_component: js` field selections from `SplitOperation`
                    let next_selections = fragment
                        .selections
                        .iter()
                        .filter(|selection| match selection {
                            Selection::ScalarField(field) => {
                                field.alias.is_none()
                                    || schema.field(field.definition.item).name.item
                                        != MATCH_CONSTANTS.js_field_name
                            }

View on GitHub (pinned to 668b1b85e0)

Solutions

  1. Add an explicit type condition to the module import inline fragment: ... on TypeName @module(name: "...") { ... }.
  2. Fix the generator/transform that produced the fragment so it always emits a type condition.
  3. Confirm the fragment really is a module import — plain inline fragments without a type condition safely take the default_transform_inline_fragment path.
  4. Re-run codegen after fixing so the split operations are regenerated.
  5. If the syntax looks valid, minimize the document and report a relay-compiler issue.

Example fix

// before
fragment F on Query {
  ... @module(name: "ProfilePhoto") { photo }
}
// after
fragment F on Query {
  ... on User @module(name: "ProfilePhoto") { photo }
}
Defensive patterns

Strategy: type-guard

Validate before calling

function validateModuleImportFragment(fragment) {
  if (!hasModuleDirective(fragment)) return true;
  return Boolean(
    fragment.typeCondition && fragment.typeCondition.name?.value
  );
}
// before compiling: if (!validateModuleImportFragment(frag)) throw new Error('@module inline fragment needs "on Type"');

Type guard

function hasTypeCondition(fragment) {
  return fragment.kind === 'InlineFragment' &&
    fragment.typeCondition != null &&
    typeof fragment.typeCondition.name?.value === 'string';
}

Try / catch

try {
  program = applySplitModuleImport(program);
} catch (e) {
  if (String(e.message).includes('module import inline fragment to have a type')) {
    reportDocumentError(programName, 'Module-import inline fragment is missing its "on TypeName" condition');
  }
  throw e;
}

Prevention

When it happens

Trigger: An inline fragment annotated for module import (matched via module_metadata) lacking a type condition, so fragment.type_condition is None when computing get_normalization_operation_name and building the split operation.

Common situations: Hand-written fragments using ... @module without 'on Type'; a code generator that dropped the type condition; refactors removing the type condition assuming it can be inferred from the parent type; plugins emitting typeless inline fragments.

Related errors


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