Skip to content

Directive arguments on unused fragments are not validated #1027

@trevor-scheer

Description

@trevor-scheer

Description

When an ExecutableDocument contains a fragment that is not referenced by any operation, directive argument validation is skipped entirely for that fragment. Only the "unused fragment" diagnostic is reported — unknown arguments, missing required arguments, etc. are silently ignored.

This is a problem for LSP-like tooling where individual fragments are validated in isolation (without a corresponding operation in the same document).

Reproduction

let schema = Schema::parse_and_validate(
    "directive @d(a: Int) on FRAGMENT_DEFINITION\ntype Query { f: Int }",
    "s.graphql",
).unwrap();

// Referenced by an operation — unknown arg `bad` IS reported ✅
let err = ExecutableDocument::parse_and_validate(
    &schema,
    "query { ...F }\nfragment F on Query @d(bad: 1) { f }",
    "ref.graphql",
).unwrap_err().errors.to_string();
assert!(err.contains("is not supported by `@d`"));

// NOT referenced by an operation — unknown arg `bad` is NOT reported ❌
let err = ExecutableDocument::parse_and_validate(
    &schema,
    "fragment F on Query @d(bad: 1) { f }",
    "standalone.graphql",
).unwrap_err().errors.to_string();
// Only error is: fragment `F` must be used in an operation
// No directive argument validation occurs
assert!(err.contains("is not supported by `@d`")); // FAILS

Cause

validate_with_or_without_schema (executable/validation.rs:40-48) calls:

  1. validate_operation_definitions — iterates operations only
  2. validate_fragments_used — only checks whether fragments are referenced, never validates their contents

Fragment definitions are only validated via validate_fragment_definition, which is only reachable through validate_fragment_spread — i.e., a fragment spread in an operation's selection set. Orphan fragments never enter this path.

Expected behavior

Directive arguments (and other fragment-level validation) should be reported regardless of whether the fragment is used in an operation.

Possible approaches

  1. Always validate all fragment definitions: In validate_with_or_without_schema (or validate_executable_document), iterate document.fragments directly and call validate_fragment_definition for each, independent of reachability from operations. This is the simplest fix but changes the existing behavior for all callers.

  2. Configurable validation via an option: Add an option to ExecutableDocument::validate (or a new method) that controls whether unused fragments receive full validation. This would let LSP tooling opt in without affecting the default behavior for spec-strict use cases.

  3. Addressed as part of ExecutableDocumentBuilder (Add fragments to executable_doc.validate() #1004 / feat(compiler): Introduce ExecutableDocumentBuilder for operations with multiple sources #1017): The builder already needs to handle multi-source fragments. Validation of independently-defined fragments could be a natural part of that API, where the notion of "unused" doesn't apply in the same way.

Related: #1004, #988

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions