gax-php icon indicating copy to clipboard operation
gax-php copied to clipboard

[1.34.1] Maximum call stack size of 8339456 bytes (zend.max_allowed_stack_size - zend.reserved_stack_size) reached. Infinite recursion?

Open czoIg opened this issue 1 year ago • 23 comments

Environment details

  • OS: Debian Bullseye
  • PHP version: 8.3
  • Package name and version: google/gax:v1.34.1 (1.34.0 was working without errors)

Steps to reproduce

  1. Error seems a little random, but occurs always at the same place. I couldn't reproduce it myself, because for many thousands requests, there's like 5-10 errors per day.

Code example

Exception trace

{
    "class": "Error",
    "message": "Maximum call stack size of 8339456 bytes (zend.max_allowed_stack_size - zend.reserved_stack_size) reached. Infinite recursion?",
    "code": 0,
    "file": "/app/vendor/google/gax/src/CredentialsWrapper.php:247",
    "trace": [
        "/app/vendor/grpc/grpc/src/lib/UnaryCall.php:43",
        "/app/vendor/grpc/grpc/src/lib/BaseStub.php:295",
        "/app/vendor/grpc/grpc/src/lib/BaseStub.php:545",
        "/app/vendor/google/gax/src/Transport/GrpcTransport.php:228",
        "/app/vendor/google/gax/src/GapicClientTrait.php:641",
        "/app/vendor/google/gax/src/Middleware/CredentialsWrapperMiddleware.php:58",
        "/app/vendor/google/gax/src/Middleware/FixedHeaderMiddleware.php:68",
        "/app/vendor/google/gax/src/Middleware/RetryMiddleware.php:89",
        "/app/vendor/google/gax/src/Middleware/RequestAutoPopulationMiddleware.php:73",
        "/app/vendor/google/gax/src/Middleware/OptionsFilterMiddleware.php:61",
        "/app/vendor/google/gax/src/GapicClientTrait.php:606",
        "/app/vendor/google/cloud-pubsub/src/V1/Gapic/PublisherGapicClient.php:909",
        "/app/vendor/google/cloud-core/src/ExponentialBackoff.php:97",
        "/app/vendor/google/cloud-core/src/GrpcRequestWrapper.php:135",
        "/app/vendor/google/cloud-core/src/GrpcTrait.php:81",
        "/app/vendor/google/cloud-pubsub/src/Connection/Grpc.php:284",
        "/app/vendor/google/cloud-pubsub/src/Topic.php:527",
        "/app/vendor/google/cloud-pubsub/src/Topic.php:482",
        ... (app-specific trace)
    ]
}

czoIg avatar Sep 09 '24 08:09 czoIg

We also encountered this problem. PHP 8.3 has new feature and tries to detect the recursion. This feature likely doesn't work correctly with the gRPC PHP extension.

The workaround is to disable stack size checks in the PHP INI settings: zend.max_allowed_stack_size: -1.

pulzarraider avatar Sep 11 '24 06:09 pulzarraider

We also encounter this issue, incidentally. The possibility of disabling the stack size check, and avoiding the occurrence of the Error, does not mean the code works properly/efficiently. The comment in CredentialsWrapper::getAuthorizationHeaderCallback, where our infinite recursion errors originate, also suggests awareness about error-prone logic.

jhoffland avatar Sep 20 '24 11:09 jhoffland

Happens thousands of times per day on my side.

romanstingler avatar Sep 26 '24 07:09 romanstingler

Same issue here, happens with gax 1.34.[01] on php 8.3 with swoole/grpc.

hydrapolic avatar Oct 28 '24 11:10 hydrapolic

Still an issue with gax 1.35.0 on php 8.3

Jakoffe avatar Nov 07 '24 10:11 Jakoffe

We worked around this by wrapping the call in

    try {
    }
    catch (\Throwable $exception) {
    }

seanBlommaert avatar Dec 01 '24 00:12 seanBlommaert

This issue Segmentation Fault in 1.68.0 for PHP 8.3 states: Works with grpc 1.66 and that seems to be the case. So if you have the possibility to (downgrade to) using 1.66.0 it can also be used as a workaround

erik-treehouse avatar Dec 02 '24 08:12 erik-treehouse

We worked around this by wrapping the call in

    try {
    }
    catch (\Throwable $exception) {
    }

Which call

Komche avatar Dec 04 '24 10:12 Komche

Which call

We have done something like this:

    // Execute API query and return the result.
    try {
      $client = $this->googleQueryFactory->getClient();
      $response = $client->runReport($report_parameters);
    }
    catch (\Throwable $exception) {
    }

It doesn't actually fix the issue, but at least allows loading the rest of the page.

seanBlommaert avatar Feb 03 '25 10:02 seanBlommaert

I ran into the Maximum call stack size exceeded error in getAuthorizationHeaderCallback, which seemed to be caused by recursion. I changed the code to execute the callback function directly instead of returning it as a closure, and it resolved the issue:

Original

return function () use ($audience) {
    // Token fetching and validation logic  
};

Modified

$callback_fn = function () use ($audience) {  
    // Token fetching and validation logic  
};  
 
return $callback_fn();  

~This worked for my case and seems to have fixed the issue. I’m considering making a pull request, but I’m not entirely sure if this is the right approach. I haven’t done any performance benchmarking or tested it thoroughly in other scenarios.~

~Is there a reason the closure is necessary?~

haizadvnet avatar Feb 14 '25 03:02 haizadvnet

$callback_fn = function () use ($audience) {  
    // Token fetching and validation logic  
};  
 
return $callback_fn();  

This looks like you may just run the "Token fetching and validation logic" outside of the callback and return its result without using callback at all.

czoIg avatar Feb 14 '25 11:02 czoIg

This looks like you may just run the "Token fetching and validation logic" outside of the callback and return its result without using callback at all.

You're absolutely right. Previously, I was working on a different project that didn’t require authentication, so it seemed fine at the time.

We've now fixed it by adding a retry mechanism in case of a Throwable error:

$maxRetries = 3;
$attempt = 0;
$delay = 100000; // Initial delay in microseconds (100ms)

while ($attempt < $maxRetries) {
    try {
        $attempt++;
        // Execute the operation that may fail
    } catch (\Throwable $e) {
        if ($attempt >= $maxRetries) {
            throw $e; // Re-throw the exception after max retries
        }

        var_dump('Retrying... Attempt: ' . $attempt);
        usleep($delay); // Wait before retrying
        $delay *= 2; // Exponential backoff
    }
}

This ensures that if an error occurs, the request is retried up to three times with an exponential backoff before failing completely.

haizadvnet avatar Feb 20 '25 03:02 haizadvnet

I found the solution Friends!!!!!!!! (google ads php)

return (new GoogleAdsClientBuilder()) ->withOAuth2Credential($oAuth2Credential) ->fromFile(storage_path('google/google_ads_php.ini')) ->withTransport('rest') ->build();

you need add "->withTransport('rest') "

avetiq-dev avatar Feb 20 '25 12:02 avetiq-dev

haizadvnet Could you clarify where you added the retry mechanism you outlined?

jastraat avatar Mar 18 '25 12:03 jastraat

haizadvnet Could you clarify where you added the retry mechanism you outlined?

You may try adding this to your function that interacts with gax-php, as it handles API calls for various Google Cloud services. In my case, it's Firestore using gRPC.

Example:

$maxRetries = 3;
$attempt = 0;
$delay = 100000;

while ($attempt < $maxRetries) {
    try {
        $attempt++;
        return $this->firestore->collection($this->collectionName)->document($document)->set(...);
    } catch (\Throwable $e) {
        if ($attempt >= $maxRetries) {
            throw $e; // Re-throw the exception after max retries
        }
        usleep($delay); // Wait before retrying
        $delay *= 2; // Exponential backoff
    }
}

haizadvnet avatar Mar 19 '25 01:03 haizadvnet

Any plans to fix it for PHP 8.3 - 8.4?

krystianlaubach avatar Apr 02 '25 14:04 krystianlaubach

@krystianlaubach I've posted the issue to the grpc repo here: https://github.com/grpc/grpc/issues/39509

Will try to take a look. In the meantime, has anyone tried this with the latest gRPC version (at this time, 1.72)?

bshaffer avatar May 07 '25 19:05 bshaffer

I found the solution Friends!!!!!!!! (google ads php) you need add "->withTransport('rest') "

This is not a solution, but a workaround. You're using REST instead of gRPC. There's nothing wrong with doing that, but it isn't a proper solution.

We've now fixed it by adding a retry mechanism in case of a Throwable error

Again not a fix, but a workaround. You're now catching the error and retrying it, and as it's intermittent, it seems to work.

I'll keep investigating here.

bshaffer avatar May 07 '25 19:05 bshaffer

grpc/grpc#39509

Yes I have on Acquia with php8.3 and I get this error.

Brayn7 avatar May 12 '25 15:05 Brayn7

Getting the same error with the Firebase SDK and Firestore using gRPC.

Fix would be great!

EDIT: workaround with setting the zend.max_allowed_stack_size to -1 works but I'd rather not do this

gijsbeijer avatar Aug 06 '25 14:08 gijsbeijer

google can only add code with their AI and cant fix code, they are probably waiting for real people from the community fixing it

romanstingler avatar Sep 17 '25 07:09 romanstingler

@bshaffer I know it's a workaround, but https://github.com/googleapis/gax-php/pull/632 would be helpful in this situation. gRPC is notoriously problematic in dev environments (usually after an update).

sl0wik avatar Nov 21 '25 19:11 sl0wik

posted analysis on https://github.com/grpc/grpc/issues/39509 We might need to fix usage of the authhttphandler , see analysis for details

pawbhard avatar Nov 26 '25 17:11 pawbhard