Describe the bug
Several connection resolvers short-circuit when the caller may not see the results and return a hand-built array instead of running the resolver. Those arrays have no pageInfo key:
// includes/connection/class-orders.php — get_customer_order_connection()
if ( empty( $customer->billing['email'] ) && ( empty( absint( $customer->ID ) ) ) ) {
return [
'nodes' => [],
'edges' => [],
];
}
// includes/connection/class-coupons.php — resolve()
if ( ! self::should_execute() ) {
return [
'edges' => [],
'nodes' => [],
];
}
// includes/connection/class-customers.php — resolve() (both connections)
if ( ! self::should_execute() ) {
return [
'nodes' => [],
'edges' => [],
];
}
// includes/connection/class-orders.php — get_customer_refund_connection()
$empty_results = [
'pageInfo' => null, // explicitly null rather than absent
'nodes' => [],
'edges' => [],
];
Every connection type declares pageInfo as NON-NULL, so the absent/null key resolves to null and graphql-php raises:
Cannot return null for non-nullable field "RootQueryToOrderConnection.pageInfo".
Because a non-null field error propagates to its parent, the entire connection comes back null — and with GRAPHQL_DEBUG off the client only sees a bare "Internal server error".
The practical effect is that selecting nodes or edges { cursor } works fine, but selecting pageInfo destroys the whole query. On a headless storefront this took down every "my account" page for signed-in customers, with no indication of the cause.
To Reproduce
Against any store, unauthenticated (no login needed — a guest hits the same bail-out):
{ orders(first: 1) { pageInfo { hasNextPage } } }
{
"errors": [{
"message": "Internal server error",
"path": ["orders", "pageInfo"]
}],
"data": { "orders": null }
}
With GRAPHQL_DEBUG enabled the real message appears:
"debugMessage": "Cannot return null for non-nullable field \"RootQueryToOrderConnection.pageInfo\"."
The same query selecting anything else is fine:
{ orders(first: 1) { nodes { databaseId } } } # => { "orders": { "nodes": [] } }
{ orders(first: 1) { edges { cursor } } } # => { "orders": { "edges": [] } }
Also reproduces on coupons, and on customer { orders { pageInfo { … } } }. Control connections that do not have this bail-out are unaffected — products, posts, productCategories and variations all return pageInfo normally.
Worth noting the asymmetry between the two order paths, because it makes the bug look inconsistent: get_customer_order_connection() guards with empty( absint( $customer->ID ) ) while get_customer_refund_connection() guards with empty( $customer->ID ). For a session customer $customer->ID is 'guest' — absint('guest') is 0 (empty) but 'guest' itself is truthy — so only the orders path bails, and refunds appears healthy while orders does not.
Expected behavior
An empty connection should be a valid connection. The bail-outs should return a well-formed pageInfo alongside the empty nodes/edges, matching what AbstractConnectionResolver::prepare_page_info() produces for an empty result set:
'pageInfo' => [
'startCursor' => null,
'endCursor' => null,
'hasNextPage' => false,
'hasPreviousPage' => false,
],
A caller who is not permitted to see the results should get an empty page, not a nulled connection behind a generic server error.
Plugin Versions
- WooGraphQL Version: reproduced on both 1.0.2 and 1.0.3
- WPGraphQL Version: 2.13.0 (also checked against 2.19.0 — the
AbstractConnectionResolver diff between them is docblock-only, so this is not version-specific on the WPGraphQL side)
- WordPress Version: 7.0.3
- WooCommerce Version: 10.7.0 (HPOS enabled, sync off — not HPOS-related;
coupons and customers are affected too and neither is an order table)
Additional context
PHP 8.2. This may well be the underlying cause of #808 ("Missing pageInfo fields"), which reports the same symptom from gatsby-source-woocommerce but predates these versions.
Happy to open a PR adding the pageInfo payload to the four bail-outs if that is a welcome contribution.
Describe the bug
Several connection resolvers short-circuit when the caller may not see the results and return a hand-built array instead of running the resolver. Those arrays have no
pageInfokey:Every connection type declares
pageInfoas NON-NULL, so the absent/null key resolves to null and graphql-php raises:Because a non-null field error propagates to its parent, the entire connection comes back
null— and withGRAPHQL_DEBUGoff the client only sees a bare"Internal server error".The practical effect is that selecting
nodesoredges { cursor }works fine, but selectingpageInfodestroys the whole query. On a headless storefront this took down every "my account" page for signed-in customers, with no indication of the cause.To Reproduce
Against any store, unauthenticated (no login needed — a guest hits the same bail-out):
{ orders(first: 1) { pageInfo { hasNextPage } } }{ "errors": [{ "message": "Internal server error", "path": ["orders", "pageInfo"] }], "data": { "orders": null } }With
GRAPHQL_DEBUGenabled the real message appears:The same query selecting anything else is fine:
{ orders(first: 1) { nodes { databaseId } } } # => { "orders": { "nodes": [] } } { orders(first: 1) { edges { cursor } } } # => { "orders": { "edges": [] } }Also reproduces on
coupons, and oncustomer { orders { pageInfo { … } } }. Control connections that do not have this bail-out are unaffected —products,posts,productCategoriesandvariationsall returnpageInfonormally.Worth noting the asymmetry between the two order paths, because it makes the bug look inconsistent:
get_customer_order_connection()guards withempty( absint( $customer->ID ) )whileget_customer_refund_connection()guards withempty( $customer->ID ). For a session customer$customer->IDis'guest'—absint('guest')is0(empty) but'guest'itself is truthy — so only the orders path bails, and refunds appears healthy while orders does not.Expected behavior
An empty connection should be a valid connection. The bail-outs should return a well-formed
pageInfoalongside the emptynodes/edges, matching whatAbstractConnectionResolver::prepare_page_info()produces for an empty result set:A caller who is not permitted to see the results should get an empty page, not a nulled connection behind a generic server error.
Plugin Versions
AbstractConnectionResolverdiff between them is docblock-only, so this is not version-specific on the WPGraphQL side)couponsandcustomersare affected too and neither is an order table)Additional context
PHP 8.2. This may well be the underlying cause of #808 ("Missing pageInfo fields"), which reports the same symptom from gatsby-source-woocommerce but predates these versions.
Happy to open a PR adding the
pageInfopayload to the four bail-outs if that is a welcome contribution.