Skip to content

GraphQl: Prevent infinite query loop on unexpected responses - #355

Draft
BigRoy wants to merge 1 commit into
developfrom
bugfix/graphql-infinite-pagination-loop
Draft

GraphQl: Prevent infinite query loop on unexpected responses#355
BigRoy wants to merge 1 commit into
developfrom
bugfix/graphql-infinite-pagination-loop

Conversation

@BigRoy

@BigRoy BigRoy commented Sep 12, 2026

Copy link
Copy Markdown
Member

Bug

Two unexpected (but valid-JSON) GraphQl responses make GraphQlQuery.query() / continuous_query() loop forever, sending the same request to the server again and again:

  1. A response without errors where data is null (or missing).
  2. A page reporting hasNextPage: true without an endCursor (e.g. an empty page).

Neither is produced by a healthy server in normal use, but when it happens the client hangs (and hammers the server) instead of failing with an error that can be debugged.

Cause

  1. parse_result(None, ...) returns immediately, so no field changes its need_query state and the while self.need_query loop repeats the identical query.
  2. The cursor was set to None, which means "start from the beginning", so pagination restarted from the first page. (On the very first page it instead raised the misleading "Cursor didn't change" error.)

Fix

  1. Query sending is moved into one _query_data helper (it was copy-pasted three times). It raises GraphQlQueryError including the response, query and variables when data is missing.
  2. When another page is reported without a cursor, pagination of that field stops with a warning containing the field path and number of received items.

Reproduce

Not reproducible against a healthy server. tests/test_graphql_infinite_loop.py uses a scripted fake connection returning these responses; on develop the fake connection raises "Infinite query loop" after 10 identical requests.

Testing notes

  • Run pytest tests/test_graphql_infinite_loop.py.
  • Regular queries are unaffected, e.g. get_folders, get_versions, get_events return the same results as before.

🤖 Generated with Claude Code

- Response with 'data: null' did not change pagination state, so the
  same query was sent again forever. Raise GraphQlQueryError with the
  query instead.
- Page reporting another page without a cursor reset the cursor to
  'None', so pagination started again from the first page. Stop
  pagination of the field with a warning.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Comment thread ayon_api/graphql.py
if change_cursor and self._need_query:
if new_cursor is None:
# Without cursor the pagination would start from beginning
log.warning(

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this raise GraphQlQueryError?

@BigRoy BigRoy Sep 14, 2026

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm actually not sure how this could ever happen. This could happen if the result returned no cursor, or? So it'd be a response issue then, perhaps? 🤔 Is the GraphQlQueryError also for response issues, or query-specific problem?

But - yes... probably?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is only for nested queries, which we don't use, so I believe AI in this case.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants