Same UUID assigned to different pages

Hi! I’m encountering a bug on a live client site where multiple different pages have apparently been assigned identical UUIDs, which leads to pages fields not working properly. Is this a known bug? Do you have any idea what might have been the cause of this error and what steps I can take to fix it?

The site has been running on Kirby 4.6.1. I’m now updating to the most recent version. As a quick fix I’m temporarily changing the pages field to store IDs instead of UUIDs.

All the best!

Edit: even with stored IDs the pages field seems to get confused and stores the ID of the wrong page.

Can you reproduce the issue in a Starterkit of the same Kirby version? Could it be that those duplicated UUID were created through page duplication? As far as I recall, that used to be an issue but not exactly sure which update fixed that, guess that was long before 4.6

Had same issue when on 4.4.1. Also some weird folders called “new” popping up when drafts where created.

The steps I took was

  1. I had to update a custom custom plugin which made use of the uuid in a property. Might not apply to you but do a search for uuid in blueprints and plugins.

I changed this line:
return $this->model()->uuid()->id();

To this
return $this->model()->content()->get("uuid")->value();

  1. update to 4.7

  2. Clean the cache, including the uuid cache! On the live server I had to do that by ssh as ftp didn’t handle the big number of files very well

  3. Finally you can force Kirby to regenerate the uuid cache in one go, there’s a method for that which I can’t recall at the moment

This script might help to identify duplicates and verify you don’t have any left after the remedy. Put it in a template. It’s originally based on something I found on this forum, should credit someone but it was a while ago so I don’t know who.

<?php
  
  // Use Kirby CMS content directory
  $directory = kirby()->root('content');
  
  $uuidPattern = '/^Uuid:\s*([a-zA-Z0-9-]+)/m';
  $uuids = [];
  $duplicates = [];
  
  function scanDirectory($dir)
  {
      $files = [];
      $items = scandir($dir);
      foreach ($items as $item) {
          if ($item === '.' || $item === '..') continue;
          $path = $dir . DIRECTORY_SEPARATOR . $item;
          if (is_dir($path)) {
              $files = array_merge($files, scanDirectory($path));
          } elseif (pathinfo($path, PATHINFO_EXTENSION) === 'txt') {
              $files[] = $path;
          }
      }
      return $files;
  }
  
  // Scan content folder
  $txtFiles = scanDirectory($directory);
  
  // Detect duplicates
  foreach ($txtFiles as $file) {
      $content = file_get_contents($file);
      if (preg_match($uuidPattern, $content, $matches)) {
          $uuid = $matches[1];
          if (isset($uuids[$uuid])) {
              $duplicates[$uuid][] = $file;
          } else {
              $uuids[$uuid] = [$file];
          }
      }
  }
  
  // Output results as HTML
  echo '<main class="wrapper">';
  echo '<h1>Duplicate UUID Checker</h1>';
  
  if (!empty($duplicates)) {
      echo "<h2>Duplicate UUIDs found:</h2><ul>";
      foreach ($duplicates as $uuid => $files) {
          echo "<li><strong>UUID: $uuid</strong><ul>";
          foreach ($files as $file) {
              echo "<li>$file</li>";
          }
          echo "</ul></li>";
      }
      echo "</ul>";
  } else {
      echo "<p>No duplicate UUIDs found.</p>";
  }

Might be related to this issue:

Basically, don’t use page.uuid in your blueprints for now.

Interesting, thanks a lot for the hint. I’ve had the __new__ issue as well.

I’m actually using slug: "{{ site.time }}-{{page.title.slug}}" in a custom page creation dialog, but displaying the page’s UUID with text: "{{ page.permalink }}" in a text field in the sidebar. I’ll remove that field for now.

Thank you for this! Turns out there’s way more same UUIDs than I thought. I’ll clean the cache and regenerate UUIDs. The method is Uuids::generate('pages') (see Regenerate page uuids - #2 by texnixe)

Meanwhile I’ve upgraded Kirby to the latest version and removed the field where the UUID is shown to the user. Let’s see if the issue persists.

1 Like

Is there actually a simple method for this? I’ve only found Uuids::generate() | Kirby CMS – which however only generates UUIDs if they are missing.

I’m using this script in a route:

$articles = page('kategorien/baustoffe')->index()->filterBy('intendedTemplate', 'artikel');
foreach ($articles as $article) {
    $article->update([
        'uuid' => Uuid::generate(),
    ]);
};

but it doesn’t seem to be doing anything. What am I missing?

Also: I’ve updated to 4.8.0. The issue of UUIDs assigned multiple times persists for pages created since the update.

Depends on what you are trying to do here. Do you want to replace existing uuids for all articles? In that case my guess is that uuids are not regenerated if one already exists. (if it did replace existing, it would break any reference to that page in other content files).

In my case I Identified the duplicates which was just a couple of pages, and just removed the uuid from the content file. Kirby will generate a new uuid automatically when needed. Or you can call Uuid::generate like you did to speed that up I assume. Another way around it could be to loop your articles and call $article->save() instead, which (I think) also would generate a missing uuid.

To rebuild the uuid cache after deleting it, I think it is Uuid::populate() which you can run once if you like. I don’t think that is actually needed but may be good to do in a controlled manner to get everything up to speed.

Thanks for the answer! Yes, I’m trying to remove and regenerate UUIDs for all existing pages. There is only one place where the reference is needed in a pages field – which is broken anyways because of the many broken UUIDs – so I’m not concerned about references being lost.

There are hundreds of pages with duplicate/repeatedly assigned UUIDs, so manually removing from the content file is not really an option. I thought that updating the page using $page->update() would overwrite existing data in the content file but maybe that’s not the case with UUIDs?

To remove all uuids you can try this in your route:

foreach ($articles as $article) {
  // Using save instead of update will be way faster than using update
  $article-> save([
        'uuid' => null,
    ]);

}

// Flush the uuid cache when all ids have been removed
Uuids::cache()->flush();

After clearing those uuids, you should be able to generate new uuids for the once you removed, in a loop like you did in your original code. Or even simpler, just call this once:

// Creates all **missing** uuids for all pages
Uuids::generate();

// Rebuild the cache
Uuids::populate()

Optional way, which you call once to clear all uuids for all content or specified type. I did not test it.

Uuids::each(
  function ($model) {
    $model->save(['uuid' => null]);
  }, 
  'all'); //models: all|page|file|block|struct

Uuids::cache()->flush();

// Regenerate
Uuids::generate();

// Rebuild the cache
Uuids::populate()

If you have very many pages and do this in a route you might have troubles with hitting max execution, then you can do this in a script instead.

This seems to have worked to reassign existing UUIDs. Let’s see if the multiples problem persists with new pages. Thank you!

You can also use the Kirby CLI tool to fix up the UUIDs, without needing to write a script.