Problem/Motivation
We sometimes see this error message in our server logs:
PHP message: PHP Fatal error: Call to a member function getTimestamp() on null in /var/www/current/web/core/modules/page_cache/src/StackMiddleware/PageCache.php on line 257
Looking at the code in question, it is clear that it doesn't take into account that getExpires() may return null, as seen from Symfony's documentation
Proposed resolution
See @msgph's patch in #3 that handles this.
Original issue
https://travis-ci.org/mohankumargupta/drupal8travisinstall/builds/81727062
error:
PHP Fatal error: Call to a member function getTimestamp() on a non-object in /home/travis/build/mohankumargupta/drupal8travisinstall/web/core/modules/page_cache/src/StackMiddleware/PageCache.php on line 226
Help!
| Comment | File | Size | Author |
|---|---|---|---|
| #49 | 2573807-49.patch | 3.35 KB | sam152 |
| #49 | 2573807-49_TEST-ONLY.patch | 2.34 KB | sam152 |
| #49 | interdiff.txt | 1.57 KB | sam152 |
| #44 | 2573807-44.patch | 3.33 KB | sam152 |
| #44 | 2573807-44_TEST-ONLY.patch | 2.32 KB | sam152 |
Comments
Comment #2
badrange commentedWe also see this error, now with Drupal Core 8.0.5:
FastCGI sent in stderr: "PHP message: PHP Fatal error: Call to a member function getTimestamp() on null in /var/www/current/web/core/modules/page_cache/src/StackMiddleware/PageCache.php on line 257" while reading response header from upstream, client: 127.0.0.1Comment #3
msgph commentedPossible fix for this
Comment #4
badrange commentedComment #5
dagmarComment #7
jcnventura@msgph: looks good, but it would be good to add 1 line of comment saying that getExpires returns Null when the expires header is not set.
Also, it needs tests...
Comment #8
msgph commentedComment #9
jcnventuraComment #11
prateekjain commentedComment #12
prateekjain commentedComment #13
msgph commentedComment #14
jcnventuraNeeds tests.
Comment #16
killua99 commentedYes, this is clearly broken my server too.
The patch need reroll for 8.2.x
Comment #17
heikki commentedI ran into the same error.
I was using "domain" module and got the error only on secondary domain, so this might be caused by that, but I don't have time to look into it.
Also the error only appeared for anonymous users.
Anyway, here is the 8.2.x reroll for the patch.
Comment #18
20th commentedThis still needs tests, I guess.
Two ternary operators in a row make code a bit hard to understand, so I have rearranged the condition a little bit. But now, after pocking in code and testing, I'm wondering if there is a logic error in this code?
The condition in
will almost always be TRUE. The only case when it becomes FALSE and cache of the page is not set is when the response is a "client error" and the value of the
cache_ttl_4xxsetting is set to 0.Intuitively, I would think that if the
Expiresheader of a page is set to a past date, it means that the page is already expired and should not be cached. But this current behavior may have something to do with the internals of the cache system that I do not fully understand.For context, in its original form this check was added in #2257709: Remove the interdependence between the internal page cache and management of the Cache-Control header for external caches.
Comment #21
20th commentedComment #25
20th commentedComment #29
20th commentedTest-only patches failed as expected.
Code in 8.2.x and 8.3.x branches has diverged and requires two different patches (which I have not noticed at first).
Comment #30
killua99 commentedThe path that 20th provide is more elegant.
Using it. No errors at the moment.
Comment #32
mglamanRan into this when returning a RedirectResponse that did not have the URL in the metadata and had page expiration set to a day, with `page_cache` enabled.
Comment #33
dagmarNice to see the test. However I think we should have a least a kernel tests that trigger this error in a real request.
Comment #34
wim leersComment #35
mfbThis bug is still relevant on Drupal 8.4.x
In SecureLogin module I have to work around by calling
$response->setExpires();on a cacheable redirect.Comment #37
sherakama commentedRe-roll of #25 for Drupal 8.3.6
Comment #38
mglamanRunning tests for #37, there are tests and removing that tag.
EDIT: Also, we're experiencing this in 8.3 as well, still.
Comment #39
aries commentedA test scenario that I'd add to the unit test:
this throws the an error when the $header is populated with Expires.
Comment #40
sherakama commentedRe-roll for 8.3.7.
Comment #41
gmaxwelled commentedComment #42
nicrodgersPatch in #40 appllies to 8.4.0 and fixes the problem for me, thanks!
Comment #43
freelockThis issue seems to happen in 8.4.0 with Redirect module, on paths that have been redirected. (It worked fine in 8.3.x), as mentioned in #2915430. Patch from 40 applies with an offset, and fixes the issue.
Comment #44
sam152 commentedI also ran into this upgrading to 8.4 with the redirect module.
I agree with #33, the current tests reach into the internal implementation of
Drupal\page_cache\StackMiddleware\PageCachewhich we shouldn't really do and it also doesn't prove what kind of real response can trigger this code path.I've done some analysis and found that the offending code is in
\Drupal\Core\EventSubscriber\FinishResponseSubscriber:The two methods responsible for setting the "Expire" header are
setResponseCacheableandsetResponseNotCacheable. So the trick to generating a problematic response is:isCacheControlCustomized.Under those circumstances, both method calls are skipped and the response is sent with no
Expireheader.Comment #45
sam152 commentedComment #47
kingdutchThe patch looks good and changes only the minimal amount of code to handle the problem. It only affects the behaviour in the error case and leaves other scenarios untouched.
It introduces no new coding standards issues. One could argue that
getCacheableWithCustomCacheControlneeds a return type but there are other one line methods in the same file that don't have this so I'm using the precedent there to determine it's unneeded.With the above and the fact that the patch successfully fixes the issue on one of our production sites I'm marking this as RTBC.
Comment #48
wim leersI only have two super silly nits, which aren't even worth un-RTBC'ing this for.
Nit: s/CacheableWith/CacheableResponseWith/
Nit: s/response custom/response with custom/
Comment #49
sam152 commentedHappy to fix the feedback! Thanks for the review.
Comment #50
wim leers👌
Comment #52
alexpottCreditting @Wim Leers and @jcnventura because their reviews affected the patch directly. Thanks to everyone else who reported that the patch successfully fixed issues in production sites.
Comment #53
alexpottCommitted and pushed 505f3bf8d4 to 8.5.x and 8d5dd3334e to 8.4.x. Thanks!
As a bug fix with no BC implications backported to 8.4.x
Comment #56
alan d. commentedWe just discovered this happening on our site too after updated from 8.3.x to 8.4.0 for logged out users only.
Appears to be a new issue related to the 8.4.0 release so tagging as such.
The commit 8d5dd33 from comment #55 applied cleanly to the tagged release 8.4.0 and fixed the issue :)
Comment #57
tinny commentedThis happened on our site after upgrading to 8.4.0 and using Paragraphs module where anonymous users didn't have permission to view the Paragraph.
After enabling the 'view' permission and clearing cache it worked.
Comment #58
afireintheattic commentedAfter upgrading to 8.4.0 we started noticing that redirects were failing for anonymous users. Applied the patch from #49 and everything is back to normal!
Comment #59
nvakenSame here, fatal errors on redirect pages. Using 8d5dd33 fixed our troubles.
Comment #61
larowlanUpdating version
Comment #62
coredumperror commented#49 also fixed this problem for me!