demo icon indicating copy to clipboard operation
demo copied to clipboard

Tricky error related to 500 error pages

Open javiereguiluz opened this issue 6 years ago • 8 comments

Yesterday I merged #909 as an (ugly) temporary solution to a very tricky error.

How to reproduce the error:

  • Use prod and debug=0 in .env
  • Comment the DATABASE_URL config to trigger a 500 error
  • The 500 error page sometimes is styled with CSS and sometimes isn't:
    • The 500 error for this page shows styles: /en/blog/posts/lorem-ipsum-dolor-sit-amet-consectetur-adipiscing-elit
    • The 500 error for this page doesn't: /en/blog/

Can anyone spot the reason and provide a pull request to fix it? Thanks!

javiereguiluz avatar Dec 05 '18 08:12 javiereguiluz

for some reason that I don't not understand either, the generated link tag is empty, event if the asset is found.

Adding, a dump, and a var_dump shows the difference, see the screenshot below and the associated code

Screenshot

bug_link_tags

Corresponding debug code
// Symfony\WebpackEncoreBundle\Asset\TagRenderer

    public function renderWebpackLinkTags(string $entryName, string $packageName = null): string
    {
        $scriptTags = [];
        foreach ($this->entrypointLookup->getCssFiles($entryName) as $filename) {
            $scriptTags[] = sprintf(
                '<link rel="stylesheet" href="%s">',
                htmlentities($this->getAssetPath($filename, $packageName))
            );

            var_dump(
                \htmlentities($this->getAssetPath($filename, $packageName)),
                sprintf(
                    '<link rel="stylesheet" href="%s">',
                    htmlentities($this->getAssetPath($filename, $packageName))
                ),
                '<link rel="stylesheet" href="/build/css/app.4aa95248.css">',
                $scriptTags
            );

            dump(
                \htmlentities($this->getAssetPath($filename, $packageName)),
                sprintf(
                    '<link rel="stylesheet" href="%s">',
                    htmlentities($this->getAssetPath($filename, $packageName))
                ),
                '<link rel="stylesheet" href="/build/css/app.4aa95248.css">',
                $scriptTags
            );
        }

        die;

        return implode('', $scriptTags);
    }

var_dump shows an "empty" string with a length of 58... (??) and dump shows the good string. echo'ing the string provide a empty output, so no link tag is rendered, and that is the issue.

I don't understand yet, why var_dump and echo does not displays the string correctly.

If someone has already experienced something similar in the past...

kevin-verschaeve avatar Dec 05 '18 10:12 kevin-verschaeve

The reason is, that webpack-encore-bundle's EntrypointLookup already has the css file in the returnedFiles in this line: https://github.com/symfony/webpack-encore-bundle/blob/a3382c840f74db83c3df42cd811e06934c422bd9/src/Asset/EntrypointLookup.php#L65

ckrack avatar Dec 05 '18 10:12 ckrack

// webpack-encore-bundle/src/Asset/EntrypointLookup.php
    public function getCssFiles(string $entryName): array
    {
+       dump('returned files:', $this->returnedFiles);
        return $this->getEntryFiles($entryName, 'css');
    }
// ...
    private function getEntryFiles(string $entryName, string $key): array
    {
        $this->validateEntryName($entryName);
        $entriesData = $this->getEntriesData();
        $entryData = $entriesData['entrypoints'][$entryName];
        if (!isset($entryData[$key])) {
            // If we don't find the file type then just send back nothing.
            return [];
        }

        // make sure to not return the same file multiple times
        $entryFiles = $entryData[$key];
+       dump('entryfiles:', $entryFiles);
+       dump('diff entry and existing returned', array_diff($entryFiles, $this->returnedFiles));
        $newFiles = array_values(array_diff($entryFiles, $this->returnedFiles));
        $this->returnedFiles = array_merge($this->returnedFiles, $newFiles);
+       dump('wrote returned files');
+       dump('newfiles', $newFiles);
        return $newFiles;
    }

Output from calling /en/blog/

"returned files:"
array:1 [
  0 => "/build/css/app.4aa95248.css"
]
"entryfiles:"
array:1 [
  0 => "/build/css/app.4aa95248.css"
]
"diff entry and existing returned"
[]
"wrote returned files"
"newfiles"
[]

Output from calling: /en/blog/posts/lorem-ipsum-dolor-sit-amet-consectetur-adipiscing-elit

"returned files:"
[]
"entryfiles:"
array:1 [▼
  0 => "/build/css/app.4aa95248.css"
]
"diff entry and existing returned"
array:1 [▼
  0 => "/build/css/app.4aa95248.css"
]
"wrote returned files"
"newfiles"
array:1 [▼
  0 => "/build/css/app.4aa95248.css"
]

The difference is the empty vs non-empty array in the first dump.

ckrack avatar Dec 05 '18 11:12 ckrack

On /en/blog/posts/lorem-ipsum-dolor-sit-amet-consectetur-adipiscing-elit, we get an immediate exception (TableNotFoundException) due to ParamConverter, so EntrypointLookup is fresh and we get the wanted css.

On /en/blog, index is load without error, so EntryLookup fill $returnedFiles as expected; then error is thrown when really loading $post (in for loop in twig). So another request is 'launched' to handle the TwigError but we stay in the same context, with the returnedFiles previously load.

Not sure how to fix that though.

MatthieuBraure avatar Dec 05 '18 11:12 MatthieuBraure

Maybe webpack-encore can add an exception listener to call the reset method ?

MatthieuBraure avatar Dec 05 '18 11:12 MatthieuBraure

Ah, so the problem is that Symfony loads page with the CSS, but then a sub-request is started for the Exception which doesn't have the CSS as this was part of the main page.

Good catch folks 👍

sstok avatar Dec 05 '18 11:12 sstok

You guys nailed it. I’m also not sure what the best fix is. We could reset in an e caption listener... but more abstractly, if you make a sub-request that returns a full HTML page (yes, that’s uncommon outside of error pages but possible), shouldn’t the entry files cache used for that sub-request be different than the entry files cache used on the master request?

weaverryan avatar Dec 05 '18 12:12 weaverryan

You guys nailed it. I’m also not sure what the best fix is. We could reset in an e caption listener... but more abstractly, if you make a sub-request that returns a full HTML page (yes, that’s uncommon outside of error pages but possible), shouldn’t the entry files cache used for that sub-request be different than the entry files cache used on the master request?

This is what i had in mind in my fix. I think it might have an issue though - it resets for all sub-requests, not only when they return full html. So when a sub-request returns only partial html, but wants to output an additional asset in there, due to the reset all assets of the type would be output again.

ckrack avatar Dec 05 '18 13:12 ckrack

Let's close this old and edge-case issue. I haven't faced it since I reported it or I wasn't aware of facing the issue, so it's not important anymore. Thanks.

javiereguiluz avatar Feb 07 '23 18:02 javiereguiluz