What happened?
When a request arrives that PHP cannot fully parse — more form fields than max_input_vars, or a body larger than post_max_size — the warning PHP raises for it is attributed to frankenphp_handle_request() and fires between requests, inside the long-running worker script but outside the handler callback.
Under a classic SAPI (Apache mod_php, php-fpm) this warning is harmless: input parsing happens at request startup, before the script has installed any userland error handler, so the warning goes to the log and the script runs with truncated input. In worker mode the same warning fires inside a script that already has a persistent userland error handler installed. With a throwing error handler — symfony/error-handler, i.e. effectively every Symfony app running in worker mode via symfony/runtime's FrankenPhpWorkerRunner — the warning becomes an uncaught ErrorException thrown out of frankenphp_handle_request():
PHP Fatal error: Uncaught ErrorException: Warning: frankenphp_handle_request():
Input variables exceeded 5000. To increase the limit change max_input_vars in php.ini.
in vendor/symfony/runtime/Runner/FrankenPhpWorkerRunner.php:72
Two things go wrong. The in-flight request is abandoned before the application ever sees it: the client receives an HTTP 200 whose body is the fatal-error dump when display_errors is on, or an empty body when it is off — never a proper 4xx. And the worker script dies with the uncaught fatal, so FrankenPHP has to boot a replacement worker.
Because input parsing happens before any application code runs — including authentication — any unauthenticated client can kill worker scripts at will by POSTing a form with more than max_input_vars fields. We hit this in production after migrating from Apache to worker mode: a single customer with a legitimately large import form restarted a worker thread on every submit (~35 fatals over a few days across three servers). A post_max_size overflow behaves identically.
To be clear about where the bug sits: the warning itself is standard PHP behaviour in every SAPI. What is FrankenPHP-specific is where it is delivered in worker mode — inside a long-running script with a live userland error handler, and outside the request callback, at a point where the worker script has no sanctioned way to catch it, associate it with the failed request, or answer that request.
Steps to reproduce
- Run a Symfony app in worker mode with the stock symfony/runtime setup (
FrankenPhpWorkerRunner) and the default symfony/error-handler.
- Lower the limits so the repro is cheap:
max_input_vars=10, post_max_size=1M.
- POST a form with 20 fields (or a 2 MB body) to any route.
- The client gets
HTTP/1.1 200 OK with the fatal-error text as the body (empty with display_errors=Off), the log shows PHP Fatal error: Uncaught ErrorException: Warning: frankenphp_handle_request(): Input variables exceeded 10 ..., and the worker script is restarted. The application is never invoked.
Reproduced against FrankenPHP 1.12 / PHP 8.5 ZTS; also seen in production on FrankenPHP 1.12.7.
What I expected
A bad request should cost that one request an error response, not a worker restart and a bogus 200. Concretely, the worker script needs a sanctioned way to learn that request parsing failed so it can answer with a 400 and keep looping — or the warning should be delivered in a way that cannot escape as an exception from frankenphp_handle_request() itself.
Prior discussion
We first proposed handling this in symfony/runtime: masking E_WARNING while the runner is parked in frankenphp_handle_request() and answering a recorded parse warning with a plain 400 without invoking the application — symfony/symfony#65761. Nicolas Grekas's feedback was that this is a rare concern pushed onto the hot path and that a solution, possibly a new API, may belong in FrankenPHP itself — hence this issue.
In case it helps, some shapes such an API could take: frankenphp_handle_request() reports the parsing failure to the worker script (a return value, a dedicated catchable exception, or an accessor like frankenphp_request_error()), so the script can respond 400 and continue; or FrankenPHP itself answers clearly-unparsable requests with a 400 before invoking the callback, perhaps opt-in. We currently ship the error-mask-plus-400 workaround as a custom runtime runner in our application, so this is not urgent for us, but every Symfony app in worker mode is exposed to the unauthenticated worker-restart behaviour by default.
Build Type
Static binary
Worker Mode
Yes
Operating System
Ubuntu 24.04
CPU Architecture
x86_64
PHP configuration
FrankenPHP v1.12.7 PHP 8.5 Caddy v2.11.4
PHP 8.5.9 (cli) (built: Jul 31 2026 08:35:54) (ZTS gcc 16.1.0 x86_64)
Built by Static PHP <https://static-php.dev>
Zend Engine v4.5.9
with Zend OPcache v8.5.9
Relevant directives:
max_input_vars = 5000
post_max_size = 15M
upload_max_filesize = 10M
display_errors = Off (production; with On the fatal dump becomes the 200 response body)
Relevant log output
[27-Aug-2026 20:01:36 UTC] PHP Fatal error: Uncaught ErrorException: Warning: frankenphp_handle_request(): Input variables exceeded 5000. To increase the limit change max_input_vars in php.ini. in /var/www/app/vendor/symfony/runtime/Runner/FrankenPhpWorkerRunner.php:72
[27-Aug-2026 18:02:31 UTC] PHP Fatal error: Uncaught ErrorException: Warning: frankenphp_handle_request(): Input variables exceeded 5000. To increase the limit change max_input_vars in php.ini. in /var/www/app/vendor/symfony/runtime/Runner/FrankenPhpWorkerRunner.php:72
[26-Aug-2026 10:01:40 UTC] PHP Fatal error: Uncaught ErrorException: Warning: frankenphp_handle_request(): Input variables exceeded 5000. To increase the limit change max_input_vars in php.ini. in /var/www/app/vendor/symfony/runtime/Runner/FrankenPhpWorkerRunner.php:72
What happened?
When a request arrives that PHP cannot fully parse — more form fields than
max_input_vars, or a body larger thanpost_max_size— the warning PHP raises for it is attributed tofrankenphp_handle_request()and fires between requests, inside the long-running worker script but outside the handler callback.Under a classic SAPI (Apache mod_php, php-fpm) this warning is harmless: input parsing happens at request startup, before the script has installed any userland error handler, so the warning goes to the log and the script runs with truncated input. In worker mode the same warning fires inside a script that already has a persistent userland error handler installed. With a throwing error handler — symfony/error-handler, i.e. effectively every Symfony app running in worker mode via symfony/runtime's
FrankenPhpWorkerRunner— the warning becomes an uncaughtErrorExceptionthrown out offrankenphp_handle_request():Two things go wrong. The in-flight request is abandoned before the application ever sees it: the client receives an HTTP 200 whose body is the fatal-error dump when
display_errorsis on, or an empty body when it is off — never a proper 4xx. And the worker script dies with the uncaught fatal, so FrankenPHP has to boot a replacement worker.Because input parsing happens before any application code runs — including authentication — any unauthenticated client can kill worker scripts at will by POSTing a form with more than
max_input_varsfields. We hit this in production after migrating from Apache to worker mode: a single customer with a legitimately large import form restarted a worker thread on every submit (~35 fatals over a few days across three servers). Apost_max_sizeoverflow behaves identically.To be clear about where the bug sits: the warning itself is standard PHP behaviour in every SAPI. What is FrankenPHP-specific is where it is delivered in worker mode — inside a long-running script with a live userland error handler, and outside the request callback, at a point where the worker script has no sanctioned way to catch it, associate it with the failed request, or answer that request.
Steps to reproduce
FrankenPhpWorkerRunner) and the default symfony/error-handler.max_input_vars=10,post_max_size=1M.HTTP/1.1 200 OKwith the fatal-error text as the body (empty withdisplay_errors=Off), the log showsPHP Fatal error: Uncaught ErrorException: Warning: frankenphp_handle_request(): Input variables exceeded 10 ..., and the worker script is restarted. The application is never invoked.Reproduced against FrankenPHP 1.12 / PHP 8.5 ZTS; also seen in production on FrankenPHP 1.12.7.
What I expected
A bad request should cost that one request an error response, not a worker restart and a bogus 200. Concretely, the worker script needs a sanctioned way to learn that request parsing failed so it can answer with a 400 and keep looping — or the warning should be delivered in a way that cannot escape as an exception from
frankenphp_handle_request()itself.Prior discussion
We first proposed handling this in symfony/runtime: masking
E_WARNINGwhile the runner is parked infrankenphp_handle_request()and answering a recorded parse warning with a plain 400 without invoking the application — symfony/symfony#65761. Nicolas Grekas's feedback was that this is a rare concern pushed onto the hot path and that a solution, possibly a new API, may belong in FrankenPHP itself — hence this issue.In case it helps, some shapes such an API could take:
frankenphp_handle_request()reports the parsing failure to the worker script (a return value, a dedicated catchable exception, or an accessor likefrankenphp_request_error()), so the script can respond 400 and continue; or FrankenPHP itself answers clearly-unparsable requests with a 400 before invoking the callback, perhaps opt-in. We currently ship the error-mask-plus-400 workaround as a custom runtime runner in our application, so this is not urgent for us, but every Symfony app in worker mode is exposed to the unauthenticated worker-restart behaviour by default.Build Type
Static binary
Worker Mode
Yes
Operating System
Ubuntu 24.04
CPU Architecture
x86_64
PHP configuration
Relevant log output