Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

jwh's avatar
Level 2

Carbon's InvalidFormatException throws a blank page in production, and doesn't get logged. In dev, everything works fine. Why?

If I pass an invalid date to Carbon::parse($date) in my dev environment, as expected, it throws Carbon\Exceptions\InvalidFormatException. Laravel sees this, and shows me the stack trace.

If I set APP_DEBUG=false, and do the same thing, I see the standard Laravel error page, just as I'd expect. The error is logged and saved properly... all works well. For completeness, it still handles correctly with APP_ENV=production set locally too.

But on production this specific error, for whatever reason, just throws a blank page:

  • The error never gets logged to Laravel
  • There's literally no record of it anywhere in the server logs
  • The application just aborts entirely

Other exceptions are occasionally logged in production, but if this one slipped past, I'm wondering if others are getting lost too. I just thought my code was really good... (haha)

Any ideas why this would be? I've tried searching the entire codebase for InvalidFormatException handling — no sign of it. My app/Exceptions/Handler.php file has no special configuration, it's the same as Laravel's out-of-the-box.

Thanks!

0 likes
3 replies
jwh's avatar
Level 2

Just made a simple test on production to try and figure this out:

Route::get('test', function() {
     try {
         return \Carbon\Carbon::parse(
             request()->query('date', 'now')
         )->toDateString();
     } catch (\Throwable $throwable) {
         return $throwable->getMessage();
     }
 });
  • When I visit /test, I see "2022-05-07" ✅
  • When I visit /test?date=01-02-2020, I see "2020-02-01" ✅
  • When I visit /test?date=15/07/2021, I see "Could not parse '15/07/2021': Failed to parse time string (15/07/2021) at position 0 (1): Unexpected character" ✅

All good so far...

  • Next, I changed return $throwable->getMessage(); to return $throwable->thisFunctionDoesNotExist();:
  • When I visit /test?date=15/07/2021, I correctly see my application's error page

Final test:

  • Remove all try { ... } catch { ... } statements from the function...
  • ...and I just get a blank white page ❌

Can't think what to try next — any thoughts?

jwh's avatar
Level 2

After further research, there seems to be something very weird going on with Composer, PHP-FPM, and the Opcache. That's the best I've got so far...

New Relic managed to uncover this error, which would otherwise have gone completely unknown:

Exception 'Symfony\Component\ErrorHandler\Error\FatalError' with message 'Uncaught Error: Class "Monolog\Handler\StreamHandler" not found in /home/forge/website/deployments/releases/20220507190825/vendor/laravel/framework/src/Illuminate/Log/LogManager.php:175

LogManager.php:175 is the function which tries to create the emergency logger, but it was failing as the class couldn't be resolved. I tried dumping the autoloader, but no difference. And weirdly, the StreamHandler class could resolve correctly through the CLI anyway.

After restarting the PHP FPM process, suddenly the error screen started working correctly again. I then made another deployment to test something else, and right away, the problem came back.

I also realised I could fix it by clearing the PHP opcache. Which is weird, because my deployment script already restarts PHP-FPM and clears the opcache. Obviously some part of that isn't working right, or may this is just some very weird low-level opcache bug 🤷‍♂️

jwh's avatar
jwh
OP
Best Answer
Level 2

I've opened a can of worms... Managed to pin this down to being a race condition between clearing the opcache, restarting PHP-FPM, and the opcache.revalidate_freq time-out not being reached before my document root symlink updates for each atomic deployment...

For anyone else who stumbles across this in future, this makes for some very interesting reading — including an Nginx-level fix suggested by Rasmus Lerdorf himself.

Finally, here's a couple more useful resources:

Hopefully this helps someone else one day — gotta love non-reproducible bugs with error messages totally unrelated to the root cause... 🤪

Please or to participate in this conversation.