facebook/relay · error

Expected at most one handle directive, got `{handle_field_di

Error message

Expected at most one handle directive, got `{handle_field_directive:?}` and `{other_handle_field_directive:?}`.

What it means

In Reader codegen, a field may carry at most one @handle (handleField) directive. build_field_name_and_alias collects handle directives with an iterator and panics if a second one exists, because codegen cannot merge multiple handle aliases into one reader field.

Source

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

                    });
                }
            };
            result.push(Primitive::Key(self.object(object)))
        }
    }

    fn build_field_name_and_alias(
        &self,
        mut name: StringKey,
        alias: Option<WithLocation<StringKey>>,
        directives: &[Directive],
    ) -> (StringKey, Option<StringKey>) {
        let mut alias = alias.map(|alias| alias.item);
        if self.variant == CodegenVariant::Reader {
            let mut handle_field_directives = extract_handle_field_directives(directives);
            if let Some(handle_field_directive) = handle_field_directives.next() {
                if let Some(other_handle_field_directive) = handle_field_directives.next() {
                    panic!(
                        "Expected at most one handle directive, got `{handle_field_directive:?}` and `{other_handle_field_directive:?}`."
                    );
                }
                let values = extract_values_from_handle_field_directive(handle_field_directive);
                alias = alias.or(Some(name));
                name = if values.key == CODEGEN_CONSTANTS.default_handle_key {
                    format!("__{}_{}", name, values.handle).intern()
                } else {
                    format!("__{}_{}", values.key, values.handle).intern()
                }
            }
        }
        (name, alias)
    }

    fn build_fragment_spread(&mut self, frag_spread: &FragmentSpread) -> Primitive {
        if let Some(no_inline_metadata) =
            NoInlineFragmentSpreadMetadata::find(&frag_spread.directives)

View on GitHub (pinned to 668b1b85e0)

Solutions

  1. Keep only one @handle directive on the field; delete the duplicate.
  2. If you need multiple handles, split the field into aliased duplicates, one per @handle, e.g. `me @handle(key: "")` and `me2: me @handle(key: "other")`.
  3. Check git history/merge conflicts for accidentally duplicated directive lines.
  4. Verify with a schema/docs check that handlers like connection or @appendEdge only need a single handle per field.

Example fix

// before
viewer @handle(key: "connection_viewer") @handle(key: "other_viewer") { id }
// after
viewer @handle(key: "connection_viewer") { id }
Defensive patterns

Strategy: validation

Validate before calling

fn field_has_single_handle(field: &Field) -> Result<(), String> {
    let count = field.directives.iter().filter(|d| d.name.item.0 == "handle").count();
    if count > 1 { Err(format!("field {} has {} @handle directives", field.name.item.0, count)) }
    else { Ok(()) }
}

Try / catch

let result = std::panic::catch_unwind(|| build_scalar_field(...));
if result.is_err() { report("field has more than one @handle directive"); }

Prevention

When it happens

Trigger: A scalar or linked field in a fragment annotated with more than one @handle directive (e.g. `@handle(key: "") @handle(key: "")`) compiled with CodegenVariant::Reader.

Common situations: Adding a second @handle for a different connection handler and forgetting the compiler allows only one per field; merge conflicts that keep two @handle lines; copying examples with multiple handles onto a single field.

Related errors


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