Skip to content

Issue with experimental flag "remove_empty_subscription_responses" #10760

@ko-pp

Description

@ko-pp

Version Information

Server Version: 2.48.3

Environment

OSS

What is the current behaviour?

When enabling the experimental flag remove_empty_subscription_responses, and starting a subscription with no results, we get the following database query error:
FatalError cannot get array length of a non-array

Subscriptions with results don’t seem affected.

What is the expected behaviour?

We should get a response with an empty array as is the case without the flag.

How to reproduce the issue?

  1. Start Hasura with env variable HASURA_GRAPHQL_EXPERIMENTAL_FEATURES set to remove_empty_subscription_responses
  2. Make a subscription with filters so that there is no selected row
  3. cannot get array length of a non-array

Please provide any traces or logs that could help here.

Log from such an error this morning
{
    "detail": {
        "connection_info": {
            "msg": null,
            "token_expiry": null,
            "websocket_id": "c6d135c6-caa4-4d2d-a01b-c88a40778d02"
        },
        "event": {
            "detail": {
                "operation_id": "c30a0851-8cd8-4ee0-860a-be1faec429e0",
                "operation_name": null,
                "operation_type": {
                    "detail": {
                        "code": "unexpected",
                        "error": "database query error",
                        "internal": {
                            "arguments": [
                                "(Oid 705,Just (\"\\NUL\\NUL\\NUL\\SOH\\NUL\\NUL\\NUL\\NUL\\NUL\\NUL\\v\\134\\NUL\\NUL\\NUL\\SOH\\NUL\\NUL\\NUL\\SOH\\NUL\\NUL\\NUL\\DLE\\206\\ENQM#\\ETB\\167E\\DC4\\135<\\229\\175w`<\\143\",Binary))",
                                "(Oid 3807,Just (\"\\NUL\\NUL\\NUL\\SOH\\NUL\\NUL\\NUL\\NUL\\NUL\\NUL\\SO\\218\\NUL\\NUL\\NUL\\SOH\\NUL\\NUL\\NUL\\SOH\\NUL\\NUL\\NUL\\138\\SOH{\\\"cursor\\\":{},\\\"query\\\":{},\\\"session\\\":{\\\"x-hasura-user-id\\\":\\\"4\\\"},\\\"synthetic\\\":[\\\"0.0\\\",\\\"false\\\",\\\"{-1,5}\\\",\\\"4\\\",\\\"false\\\",\\\"true\\\",\\\"false\\\",\\\"REGULAR\\\",\\\"0\\\"]}\",Binary))"
                            ],
                            "error": {
                                "description": null,
                                "exec_status": "FatalError",
                                "hint": null,
                                "message": "cannot get array length of a non-array",
                                "status_code": "22023"
                            },
                            "prepared": true,
                            "statement": "SELECT  *  FROM  (SELECT  \"__subs\".\"result_id\" , \"__fld_resp\".\"root\" AS \"result\" FROM UNNEST(($1)::uuid[], ($2)::json[]) AS \"__subs\"(\"result_id\", \"result_vars\") LEFT OUTER JOIN LATERAL (SELECT  json_build_object('user_user_aggregate', \"_user_user_aggregate\".\"root\" ) AS \"root\" FROM  (SELECT  json_build_object('aggregate', json_build_object('count', COUNT(*) ) ) AS \"root\" FROM  (SELECT  1  FROM  (SELECT  *  FROM \"public\".\"user_user\"  WHERE ((EXISTS  (SELECT  1  FROM \"public\".\"user_user\" AS \"__exists_table_0\" WHERE ((((((\"__exists_table_0\".\"id\") = (((\"__subs\".\"result_vars\"#>>ARRAY['session', 'x-hasura-user-id']))::integer)) AND ('true')) AND ('true')) AND (((((\"__exists_table_0\".\"is_active\") = (('true')::boolean)) AND ('true')) AND ('true')) AND (((((\"__exists_table_0\".\"first_login\") = (('false')::boolean)) AND ('true')) AND ('true')) AND ('true')))) AND ('true'))     )) AND ((EXISTS  (SELECT  1  FROM \"public\".\"invoice_salary\" AS \"__be_1_invoice_salary\" WHERE ((((\"__be_1_invoice_salary\".\"payed_id\") = (\"public\".\"user_user\".\"id\")) AND ('true')) AND (((EXISTS  (SELECT  1  FROM \"public\".\"user_user\" AS \"__exists_table_2\" WHERE ((((((\"__exists_table_2\".\"id\") = (((\"__subs\".\"result_vars\"#>>ARRAY['session', 'x-hasura-user-id']))::integer)) AND ('true')) AND ('true')) AND (((((\"__exists_table_2\".\"is_active\") = (('true')::boolean)) AND ('true')) AND ('true')) AND (((((\"__exists_table_2\".\"first_login\") = (('false')::boolean)) AND ('true')) AND ('true')) AND ('true')))) AND ('true'))     )) AND ('true')) AND ((((\"__be_1_invoice_salary\".\"to_pay\") > (((\"__subs\".\"result_vars\"#>>ARRAY['synthetic', '0']))::numeric)) AND ('true')) AND ((EXISTS  (SELECT  1  FROM \"public\".\"invoice_invoice\" AS \"__be_3_invoice_invoice\" WHERE ((((\"__be_3_invoice_invoice\".\"id\") = (\"__be_1_invoice_salary\".\"invoice_id\")) AND ('true')) AND (((EXISTS  (SELECT  1  FROM \"public\".\"user_user\" AS \"__exists_table_4\" WHERE ((((((\"__exists_table_4\".\"id\") = (((\"__subs\".\"result_vars\"#>>ARRAY['session', 'x-hasura-user-id']))::integer)) AND ('true')) AND ('true')) AND (((((\"__exists_table_4\".\"is_active\") = (('true')::boolean)) AND ('true')) AND ('true')) AND (((((\"__exists_table_4\".\"first_login\") = (('false')::boolean)) AND ('true')) AND ('true')) AND ('true')))) AND ('true'))     )) AND ('true')) AND ((((\"__be_3_invoice_invoice\".\"abandonned\") = (((\"__subs\".\"result_vars\"#>>ARRAY['synthetic', '1']))::boolean)) AND ('true')) AND ('true'))))     )) AND (((NOT ((\"__be_1_invoice_salary\".\"state\") = ANY(((\"__subs\".\"result_vars\"#>>ARRAY['synthetic', '2']))::integer[]))) AND ('true')) AND ('true'))))))     )) AND (((\"public\".\"user_user\".\"referent_id\") = (((\"__subs\".\"result_vars\"#>>ARRAY['synthetic', '3']))::integer)) AND ((EXISTS  (SELECT  1  FROM \"public\".\"student_student\" AS \"__be_5_student_student\" WHERE ((((\"__be_5_student_student\".\"user_id\") = (\"public\".\"user_user\".\"id\")) AND ('true')) AND (((EXISTS  (SELECT  1  FROM \"public\".\"user_user\" AS \"__exists_table_6\" WHERE ((((((\"__exists_table_6\".\"id\") = (((\"__subs\".\"result_vars\"#>>ARRAY['session', 'x-hasura-user-id']))::integer)) AND ('true')) AND ('true')) AND (((((\"__exists_table_6\".\"is_active\") = (('true')::boolean)) AND ('true')) AND ('true')) AND (((((\"__exists_table_6\".\"first_login\") = (('false')::boolean)) AND ('true')) AND ('true')) AND (((((\"__exists_table_6\".\"is_staff\") = (('true')::boolean)) AND ('true')) AND ('true')) AND ('true'))))) AND ('true'))     )) AND ('true')) AND ((((\"__be_5_student_student\".\"mgp_nationality_blocked\") = (((\"__subs\".\"result_vars\"#>>ARRAY['synthetic', '4']))::boolean)) AND ('true')) AND ((((((\"__be_5_student_student\".\"need_sca_enroll\") = (((\"__subs\".\"result_vars\"#>>ARRAY['synthetic', '5']))::boolean)) AND ('true')) AND ('true')) OR (((((\"__be_5_student_student\".\"has_active_recipient\") = (((\"__subs\".\"result_vars\"#>>ARRAY['synthetic', '6']))::boolean)) AND ('true')) AND ('true')) OR ('false'))) AND ('true')))))     )) OR (EXISTS  (SELECT  1  FROM  (SELECT  count(* ) AS \"mango_pay_naturaluser_count\" FROM \"public\".\"mango_pay_naturaluser\" AS \"__be_7_mango_pay_naturaluser\" WHERE (((\"__be_7_mango_pay_naturaluser\".\"user_id\") = (\"public\".\"user_user\".\"id\")) AND ((EXISTS  (SELECT  1  FROM \"public\".\"user_user\" AS \"__exists_table_8\" WHERE ((((((\"__exists_table_8\".\"id\") = (((\"__subs\".\"result_vars\"#>>ARRAY['session', 'x-hasura-user-id']))::integer)) AND ('true')) AND ('true')) AND (((((\"__exists_table_8\".\"is_active\") = (('true')::boolean)) AND ('true')) AND ('true')) AND (((((\"__exists_table_8\".\"first_login\") = (('false')::boolean)) AND ('true')) AND ('true')) AND ('true')))) AND ('true'))     )) AND ((\"__be_7_mango_pay_naturaluser\".\"kyc_level\") = (((\"__subs\".\"result_vars\"#>>ARRAY['synthetic', '7']))::\"mgp_naturaluser_kyc_level\"))))     ) AS \"__sub\" WHERE ((\"__sub\".\"mango_pay_naturaluser_count\") = (((\"__subs\".\"result_vars\"#>>ARRAY['synthetic', '8']))::integer))     ))))))     ) AS \"_root.base\"      ) AS \"_root\"      ) AS \"_user_user_aggregate\"      ) AS \"__fld_resp\" ON ('true')      ) AS \"__multiplex\" WHERE (EXISTS  (SELECT  1  FROM \"json_each\"(\"result\")  WHERE ((json_array_length(\"value\" )) > ('0'))     ))     "
                        },
                        "path": "$"
                    },
                    "type": "query_err"
                },
                "parameterized_query_hash": null,
                "query": null,
                "request_id": "3e76a5fb-fef1-4755-b1a8-a35d95c42155"
            },
            "type": "operation"
        },
        "user_vars": {
            "x-hasura-role": "staff",
            "x-hasura-user-id": "4"
        }
    },
    "level": "error",
    "timestamp": "2025-07-29T10:26:17.819+0000",
    "type": "websocket-log"
}

Any possible solutions/workarounds you're aware of?

Disabling the feature makes the subscription work again, but we were hoping that it would reduce our Postgresql server load (see #9994).

Extra note

I wanted to make a smaller subscription directly from the web console to provide it along a shorter log but GraphiQL doesn’t work because it tries to POST to https://v1/graphql, ignoring the domain name.
I am unsure whether this warrant a new issue (the flag has no incidence on it) or is a problem on my end (perhaps a faulty browser cache)

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions