Errors after file / image upload

Dear forum,

I often encounter errors after file (or image, to be exact) uploads in the panel. Kirby seems to unsuccessfully try to delete cache files. The strange thing is: when I delete the uploaded files from the folder and upload the same set again, it sometimes works without errors, or different errors, or the same errors but on other files of the set. This seems to only happen with drafts.

I’ve seen mkdir, rmdir, scandir and unlink errors–if the error contains a path (the mkdir errors don’t show a path) I can see that it’s a file in the cache. The errors look like this:

Has anybody else encountered this?

What is your Kirby version?

What is your environment? Local, remote server? PHP version? server type?

What is in your config?

What is your Kirby version?

3.5.0

What is your environment? Local, remote server? PHP version? server type?

It happens locally (macOS Big Sur with MAMP running PHP 7.4.12 using Apache) and also on a remote server (shared Linux webhosting running PHP 7.4.13 using Apache).

What is in your config?

return [
  'cache' => [
    'pages' => [
      'active' => true
    ]
  ],
  'home' => 'projekte',
  'routes' => [
    [
      // block unwanted urls (including language specific urls)
      'pattern' => [
        '(:any)/xxx/xxx/(:any)',
        'xxx/xxx/(:any)',
        '(:any)/xxx/xxx',
        'xxx/xxx',
        '(:any)/xxx/xxx/(:any)',
        'xxx/xxx/(:any)',
        '(:any)/xxx/xxx',
        'xxx/xxx'
      ],
      'action'  => function() {
        return false;
      }
    ],
    [
      // look for project urls with or without gallery index
      'pattern' => [
        'xxx/(:any)/(:num)',
        'xxx/(:any)',
        'xxx/(:any)/(:num)',
        'xxx/(:any)'
      ],
      'language' => '*',
      'action'  => function($language, $slug, $index = 0) {
        // add index if there is none
        if (!$index) {
          // FIXME: not very resilient
          if ($language == 'de') {
            $base = 'xxx/';
          } else {
            $base = 'en/xxx/';
          };

          return go($base . $slug . '/1');

        } else {
          return site()->visit('xxx/' . $slug, $language);
        };
      }
    ],
    [
      // look for collection urls with or without gallery index
      'pattern' => [
        'xxx/(:any)/(:num)',
        'xxx/(:any)',
        'xxx/(:any)/(:num)',
        'xxx/(:any)'
      ],
      'language' => '*',
      'action' => function($language, $title, $index = 0) {
        $slug = 'xxx/xxx/' . $title;
        $collection = page($slug);

        if ($collection) {
          if ($index) {
            $galleryType = $collection->galleryType();
            $selection = $collection->selection()->yaml();
            $title = $collection->content($language)->title();

            // return virtual page based on template
            $project = new Page([
              'slug' => $slug . '/' . $index,
              'template' => 'project',
              'content' => [
                'galleryType' => $galleryType,
                'selection' => $selection,
                'title' => $title
              ]
            ]);

            // return the virtual page in the current language
            return site()->visit($project, $language);

          } else {
            // return the page in the current language
            return site()->visit($slug, $language);
          }
        };

        return false;
      }
    ],
    [
      // look for category urls
      'pattern' => '(:any)',
      'language' => '*',
      'action' => function($language, $slug) {
        // get category names in current language
        $categories = page('xxx')->content($language)->categories()->toStructure();

        $category = $categories->filter(function($element) use($slug) {
          return str::slug($element->name()) == $slug;
        });

        // make sure there is only one category
        $category = $category->first();

        if ($category) {
          // start with equal slugs
          $en_slug = $de_slug = $slug;

          // get translated slug for inactive language
          if ($language == 'en') {
            $de_slug = str::slug(page('xxx')->content('de')->categories()->toStructure()->findBy('id', $category->id())->name());
          } else {
            $en_slug = str::slug(page('xxx')->content('en')->categories()->toStructure()->findBy('id', $category->id())->name());
          }

          $translated_slug = page('xxx')->content($language)->categories()->toStructure();

          // return virtual page based on template
          $category_page = new Page([
            'slug' => $slug,
            'template' => 'portfolio',
            'translations' => [
              'en' => [
                'code' => 'en',
                'slug' => $en_slug
              ],
              'de' => [
                'code' => 'de',
                'slug' => $de_slug
              ]
            ],
            'content' => [
              'title' => $category->name(),
              'category' => $category
            ]
          ]);

          return site()->visit($category_page, $language);
        };

        return $this->next();
      }
    ],
  ],
  'smartypants' => true,
  'thumbs' => [
    'presets' => [
      '400' => ['width' => 400],
      '400@2x' => ['width' => 800],
      '1200' => ['width' => 1200],
      '1600' => ['width' => 1600],
      '1200@2x' => ['width' => 2400]
    ]
  ]
];

(I replaced the actual page names and URLs for privacy reasons.)

The second and fourth custom route apply to the page where I upload the files and the errors occur—they may have to do with this.

I also update URLs in two page models but the errors still occur when I comment the lines out.

Thank you, as always, for getting back so quickly @texnixe !

Can you actually reproduce the issue with a fresh Starterkit plus caching enabled (I can’t).

Routes may indeed be the issue here… although I don’t yet see how they would affect the root path to the cache folder not being found.

Does this only affect specific pages?

I just installed a fresh Starterkit, enabled caching, created a new album and uploaded some images—the errors appear (to my surprise) even under this condition.

The error messages are the same as before, of different types (mkdir, scandir…) and only regarding 1 to 3 varying files per upload.

Very strange.

Edit: Maybe some race condition while the cache folder is completely deleted and recreated? When I look at the metadata of the cache/pages directory I can see that its creation time is the same as the image upload time.

Thanks for testing, I assume you tested with 3.5.1?

That would surprise me, at least I don’t get the error message even if there is no cache file or even cache folder yet.

I wonder if it has to do with your setup, but no idea at all what could be causing this.

Do you have server side caching enable in Mamp?

Correct (as included in the current Starterkit).

No, and since it’s happening also on the remote server, I would think it’s not strongly connected to my local setup.

I thought of a race condition because the affected files and error messages change with every upload (maybe depending on the speed of completion of certain sub-processes).

To rule out some more reasons I changed the PHP version to 8 and also installed Kirby 3.0.0 in the Starterkit. The errors still appear in both cases.

By the way: I’m uploading 9 images at once for the testing, each having a size of 300 to 600 kb.

The files get uploaded despite the error messages, so at least this is not a severe problem—but it’s not very trust-building for my client…

Funny—a client just told me about the exact problem (Kirby 4.0.3) and in a kafkaesk moment I realized that I’m reading my own words from 3 years ago. Bottom line, it’s still happening.

Kirby flushes the cache after each single file upload (rather than after all files have been uploaded), that’s probably why you see all these rmdir/mkdir and scandir messages. Maybe it happens when disk performance is slow? So that the next process to delete a folder conflicts with the folder being created or so.

1 Like

Thanks for the answer again, that’s what I figured. It’s a shared web hosting account on STRATO and they are definitely not the fastest.