Page MenuHomePhabricator

PHP Warning: Cannot modify header information - headers already sent by (output started at /srv/mediawiki/php-1.37.0-wmf.1/includes/MediaWiki.php:817)
Closed, ResolvedPublicPRODUCTION ERROR

Description

Error
normalized_message
[{reqId}] {exception_url}   PHP Warning: Cannot modify header information - headers already sent by (output started at /srv/mediawiki/php-1.37.0-wmf.1/includes/MediaWiki.php:817)
exception.trace
from /srv/mediawiki/php-1.37.0-wmf.1/includes/WebResponse.php(74)
#0 [internal function]: MWExceptionHandler::handleError(integer, string, string, integer, array)
#1 /srv/mediawiki/php-1.37.0-wmf.1/includes/WebResponse.php(74): header(string, boolean)
#2 /srv/mediawiki/php-1.37.0-wmf.1/includes/OutputPage.php(2499): WebResponse->header(string)
#3 /srv/mediawiki/php-1.37.0-wmf.1/includes/OutputPage.php(2649): OutputPage->sendCacheControl()
#4 /srv/mediawiki/php-1.37.0-wmf.1/includes/exception/MWExceptionRenderer.php(147): OutputPage->output()
#5 /srv/mediawiki/php-1.37.0-wmf.1/includes/exception/MWExceptionRenderer.php(66): MWExceptionRenderer::reportHTML(Wikimedia\RequestTimeout\RequestTimeoutException)
#6 /srv/mediawiki/php-1.37.0-wmf.1/includes/exception/MWExceptionHandler.php(106): MWExceptionRenderer::output(Wikimedia\RequestTimeout\RequestTimeoutException, integer)
#7 /srv/mediawiki/php-1.37.0-wmf.1/includes/exception/MWExceptionHandler.php(185): MWExceptionHandler::report(Wikimedia\RequestTimeout\RequestTimeoutException)
#8 /srv/mediawiki/php-1.37.0-wmf.1/includes/exception/MWExceptionHandler.php(156): MWExceptionHandler::handleException(Wikimedia\RequestTimeout\RequestTimeoutException, string)
#9 [internal function]: MWExceptionHandler::handleUncaughtException(Wikimedia\RequestTimeout\RequestTimeoutException)
#10 {main}
Impact
Notes

This looks like a new error message as of 1.37.0-wmf.1. This may potentially be something that changed in core and not something in Parsoid.

Details

Request URL
https://en.wikipedia.org/w/rest.php/en.wikipedia.org/v3/page/pagebundle/User%3AAbyssal%2Fbla/1010244322

Event Timeline

This might be a bogus error. Cannot modify header information - headers already sent happens when header() (or a similar function) is called, but some content was already sent. This can happen for lots of different reasons, but usually it is due to a previous PHP warning/notice. Less common is intentional output (e.g. echo), or whitespace before a <?php tag. The error message also says:

output started at /srv/mediawiki/php-1.37.0-wmf.1/includes/MediaWiki.php:817

this is where the early output happened.

I should also note that there are several RequestTimeoutExceptions on logstash for the same request, so perhaps the underlying cause is a simple timeout.

The reason I filed it then (and it is being filed repeatedly again) is that this was a change in behavior - this was not being logged before at all. So, did something change in core around request timeouts? Or did something change in logging?

The reason I filed it then (and it is being filed repeatedly again) is that this was a change in behavior - this was not being logged before at all. So, did something change in core around request timeouts? Or did something change in logging?

Seems to be around the introduction of T269326

headers already sent by (output started at /srv/mediawiki/php-1.37.0-wmf.1/includes/MediaWiki.php:817)

This is a bit weird in that MWExceptionHandler::handleUncaughtException is called after doPostOutputShutdown which is called at the end of /Rest/EntryPoint::execute, presumably after the parse is complete and page sent.

https://www.mediawiki.org/wiki/Excimer says,

When the timer expires, Excimer sets a flag which is then checked by the PHP VM every time a function returns, and on every loop iteration. For CPU-intensive tasks, this is usually often enough. However, it will not interrupt long-running C functions such as MySQL connection attempts.

So maybe this is a race where the timer expires somewhere in doPostOutputShutdown, which sends headers, and then when it returns the flag has been set and the exception handler is called. It seems to happen often enough to where that doesn't seem like a great explanation though.

Idk, it's the end of my day. Maybe others have ideas?

It seems to happen often enough to where that doesn't seem like a great explanation though.

Although, looking at a trace, that does seem to be what's happening,

https://logstash.wikimedia.org/app/discover#/doc/logstash-*/logstash-deploy-2021.06.09?id=EPM_8nkBQzT5HGEiKLaC

from /srv/mediawiki/php-1.37.0-wmf.7/vendor/wikimedia/request-timeout/src/Detail/ExcimerTimerWrapper.php(97)
#0 /srv/mediawiki/php-1.37.0-wmf.7/vendor/wikimedia/request-timeout/src/Detail/ExcimerTimerWrapper.php(72): Wikimedia\RequestTimeout\Detail\ExcimerTimerWrapper->onTimeout(integer)
#1 /srv/mediawiki/php-1.37.0-wmf.7/includes/MediaWiki.php(818): Wikimedia\RequestTimeout\Detail\ExcimerTimerWrapper->Wikimedia\RequestTimeout\Detail\{closure}(integer)
#2 /srv/mediawiki/php-1.37.0-wmf.7/includes/Rest/EntryPoint.php(206): MediaWiki->doPostOutputShutdown()
#3 /srv/mediawiki/php-1.37.0-wmf.7/includes/Rest/EntryPoint.php(130): MediaWiki\Rest\EntryPoint->execute()
#4 /srv/mediawiki/php-1.37.0-wmf.7/rest.php(31): MediaWiki\Rest\EntryPoint::main()
#5 /srv/mediawiki/w/rest.php(3): require(string)
#6 {main}

I tried making a few requests for a page that recently threw the warning from wtp1044 and on some occasions it timed out and others finished the parse. It really did seem to be on the cusp of the 1 minute timeout.

> time curl -v -x wtp1044.eqiad.wmnet:80 http://en.wikipedia.org/w/rest.php/en.wikipedia.org/v3/page/html/User%3AGaro3%2Fsandbox/1027299103

real	1m0.215s
user	0m0.006s
sys	0m0.011s

It's kind of unfortunate when it happens because the MWExceptionHandler calls OutputPage which tries to "modify header information" like 10 times. For example, the reqId:"YMEWE8dagR74Ar8MhONfGAAAAA0",

Screen Shot 2021-06-09 at 4.08.30 PM.png (2×2 px, 1 MB)

So, as @tstarling suggests, we could maybe add a check to the MWExceptionHandler::handleUncaughtException to ignore timeouts if headers_sent(). I'll write a patch for that.

Change 699073 had a related patch set uploaded (by Arlolra; author: Arlolra):

[mediawiki/core@master] Suppress timeout reporting if headers are already sent

https://gerrit.wikimedia.org/r/699073

Change 699073 merged by jenkins-bot:

[mediawiki/core@master] Suppress timeout reporting if headers are already sent

https://gerrit.wikimedia.org/r/699073

Arlolra claimed this task.