GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — SchemaTransformer.transformSchema produces schema with dangling reference to deleted type · Issue #4133 · graphql-java/graphql-java

SchemaTransformer.transformSchema produces schema with dangling reference to deleted type #4133

New issue
Copy link
New issue
Copy link
Closed
Closed
SchemaTransformer.transformSchema produces schema with dangling reference to deleted type#4133
Copy link
Labels
keep-openTells Stale Bot to keep PRs and issues openTells Stale Bot to keep PRs and issues open

Description

@alf
alf
opened on Oct 10, 2025
Issue body actions

Describe the bug
We've implemented a visitor that prunes our schema based on the presence of a directive.
This has worked well until quite recently, but has suddenly started failing with graphql.AssertException: Assert type Rom not found in schema.

I've analyzed the problem and reduced the schema and our visitor down to the minimal needed to reproduce the issue. Note that our actual implementation also handles other schema elements, but I don't think this is relevant for the issue at hand.

To Reproduce

Given the following schema, our visitor crashes with AssertException:

directive @remove on FIELD_DEFINITION

type Query {
  rental: Rental @remove
  customer: Customer
}

type Store {
  inventory: Inventory @remove
}

type Inventory {
  store: Store @remove
}

type Customer {
  rental: Rental
  payment: Payment @remove
}

type Payment {
  inventory: Inventory @remove
}

type Rental {
  id: ID
  customer: Customer @remove
}

Our visitor is defined as follows:

    class SchemaPruningVisitor extends GraphQLTypeVisitorStub {
        @Override
        public TraversalControl visitGraphQLFieldDefinition(GraphQLFieldDefinition node, TraverserContext<GraphQLSchemaElement> context) {
            if (node.hasAppliedDirective("remove")) {
                return deleteNode(context);
            }

            return CONTINUE;
        }

        @Override
        public TraversalControl visitGraphQLObjectType(GraphQLObjectType node, TraverserContext<GraphQLSchemaElement> context) {
            if (node.getFields().stream().allMatch(field -> field.hasAppliedDirective("remove"))) {
                return deleteNode(context);
            }

            return CONTINUE;
        }
    }

Interesting observation

Note that if we flip the order of the Query.rental and Query.customer fields then our visitor no longer crashes and produces the schema we expect:

type Query {
  customer: Customer
}

type Customer {
  rental: Rental
}

type Rental {
  id: ID
}

Activity

  1. dondonz
    added
    keep-openTells Stale Bot to keep PRs and issues openTells Stale Bot to keep PRs and issues open
    on Oct 15, 2025
  2. bbakerman commented on Oct 16, 2025

    @bbakerman
    bbakerman
    on Oct 16, 2025
    Member
    More actions

    I have created a repo PR and confirm the assertion. We will look into this further

  3. andimarek commented on Oct 17, 2025

    @andimarek
    andimarek
    on Oct 17, 2025
    Member
    More actions

    @alf in which version did this actually work, meaning from which version to version did you notice it failing?

  4. alf commented on Oct 17, 2025

    @alf
    alf
    on Oct 17, 2025
    Author
    More actions

    I believe it started failing because we suddenly ended up with a schema that has the problematic structure, not because we upgraded graphql-java.

  5. andimarek commented on Oct 17, 2025

    @andimarek
    andimarek
    on Oct 17, 2025
    Member
    More actions

    @alf yes ... I just wanted to comment on this. Indeed the problem comes from your circular structure and order of your fields.

    For example: the visitor actually never visits Rental because when it hits Query.rental the field is deleted and the visitor doesn't continue traversing down a deleted element.

    And when the traverser reaches Rental again via Query.customer -> Customer. rental the Customer.rental field definition doesn't actually reference the real rental Object but uses a GraphQLTypeReference. A GraphQLTypeReference is an internal placeholder type to deal with circular references.

    This means the actual Rental is never visited and hence still references the original Customer which indirectly references Inventory. But then on other hand Inventory is actually deleted implicitly because Customer.payment is deleted.
    This causes the exception you see.

    We need to look into the best way to fix this.

  6. alf commented on Oct 17, 2025

    @alf
    alf
    on Oct 17, 2025
    Author
    More actions

    That's a good explanation, thank you.

  7. andimarek commented on Oct 17, 2025

    @andimarek
    andimarek
    on Oct 17, 2025
    Member
    More actions

    Hi @alf ... please have a look at this: https://github.com/graphql-java/graphql-java/pull/4142/files#r2441444727

    in order to make sure you will visit every Object you need to add all object types to the schema as additionalTypes. This ensures that in your example you will always visit Rental regardless of the order of the fields (or how you change them).

    The small caveat here is that you need to avoid adding the Query/Mutation/Subscription types as this will lead to another strange error.

  8. alf commented on Oct 18, 2025

    @alf
    alf
    on Oct 18, 2025
    Author
    More actions

    Nice, thank you. Is this something that the SchemaParser should be doing? That’s what we’re using to build our TDR.

  9. andimarek commented on Oct 18, 2025

    @andimarek
    andimarek
    on Oct 18, 2025
    Member
    More actions

    Is this something that the SchemaParser should be doing?

    Not sure we should do that as default as for very large schemas it increases the traversal time and is simply unnecessary if you are not deleting nodes like in your use case.

    But we probably add a helper util or so to enable "alwaysTraverseAllNodes" to not make people write this on their own and to document this case better.

    @alf would you be open to contribute this improvement?

    Reacted by Alf Lervåg
  10. alf commented on Oct 19, 2025

    @alf
    alf
    on Oct 19, 2025
    Author
    More actions

    Sounds perfect for our use case. I’ll take a shot at contributing this.

  11. alf
    added a commit that references this issue on Oct 23, 2025

    Make sure SchemaFeatureFilter visits all nodes

    3860962
  12. andimarek
    closed this as completedon Nov 10, 2025
  13. andimarek commented on Jan 19, 2026

    @andimarek
    andimarek
    on Jan 19, 2026
    Member
    More actions

    I finally found time to add a built in way to handle these edge cases better. See #4209

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    keep-openTells Stale Bot to keep PRs and issues openTells Stale Bot to keep PRs and issues open

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions

      You are visitor 01402503 — GeezerVison does not store the pages you look up.
      Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.