Skip to content

Permission bail-outs return connections without pageInfo, nulling the entire connection (orders, coupons, customers, refunds) #1023

Description

@ahmadacw

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.

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