facebook/relay · error

Expected a scalar field.

Error message

Expected a scalar field.

What it means

When a type-discriminating inline fragment (e.g. from @match or a `typename` discriminator pattern) is processed, the compiler expects its single selection to be a scalar field that serves as the type discriminator. If the first selection is not a scalar field, it panics because no scalar field exists to build the discriminator from.

Source

Thrown at compiler/crates/relay-codegen/src/build_ast.rs:2399

                        // This means we have to handle two cases:
                        // - The inline fragment only contains a TypeDiscriminator with the same
                        //   abstractKey: replace the Fragment w the Discriminator
                        // - The inline fragment contains other selections: return all the selections
                        //   minus any Discriminators w the same key
                        let has_type_discriminator = inline_frag
                            .selections
                            .iter()
                            .any(is_type_discriminator_selection);

                        if has_type_discriminator {
                            if inline_frag.selections.len() == 1 {
                                return self.build_type_discriminator(
                                    if let Selection::ScalarField(field) =
                                        &inline_frag.selections[0]
                                    {
                                        field
                                    } else {
                                        panic!("Expected a scalar field.")
                                    },
                                );
                            } else {
                                let selections = self.build_selections(
                                    context,
                                    inline_frag.selections.iter().filter(|selection| {
                                        !is_type_discriminator_selection(selection)
                                    }),
                                );
                                return Primitive::Key(self.object(object! {
                                    kind: Primitive::String(CODEGEN_CONSTANTS.inline_fragment),
                                    selections: selections,
                                    type_: Primitive::String(
                                            self.schema.get_type_name(type_condition),
                                        ),
                                    abstract_key: Primitive::String(
                                            generate_abstract_type_refinement_key(
                                                self.schema,

View on GitHub (pinned to 668b1b85e0)

Solutions

  1. Ensure the discriminating inline fragment's first (and ideally only) selection is a scalar field such as `__typename` or the resolver payload field.
  2. Remove extra selections from the discriminator fragment or move them into a sibling fragment.
  3. Disable/review custom transforms that reorder or inject selections before build_ast.
  4. Regenerate the document instead of hand-editing compiled/output IR.

Example fix

// before
... on Note { createdAt viewer { name } }

// after
... on Note { note } // note is the scalar resolver payload field, first selection
Defensive patterns

Strategy: validation

Validate before calling

function validateDiscriminatorFragment(inlineFrag) {
  const first = inlineFrag.selections?.[0];
  if (!first || first.kind !== 'Field' || first.selections) {
    throw new Error('Type discriminator inline fragment must start with a scalar field');
  }
}

Type guard

function startsWithScalarField(inlineFrag) {
  const first = inlineFrag?.selections?.[0];
  return first != null && first.kind === 'Field' &&
    (!first.selectionSet && !first.selections);
}

Try / catch

try {
  compile();
} catch (e) {
  if (String(e).includes('Expected a scalar field')) {
    console.error('Discriminator inline fragment must have a scalar field as its first selection.');
  } else throw e;
}

Prevention

When it happens

Trigger: Compiling an inline fragment whose selections[0] is not a ScalarField — e.g. a linked field, fragment spread, or another inline fragment — in a context where Relay builds a type discriminator (typename-based branching).

Common situations: Hand-written or transformed documents where @match/module fragments have multiple or non-scalar first selections; custom transforms reordering selections; editing generated code so the discriminator field is no longer first.

Related errors


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