# Migrate to s3 failed - but only check fails

**URL:** https://meta.discourse.org/t/migrate-to-s3-failed-but-only-check-fails/394193
**Category:** Bug
**Created:** [23 januari 2026 om 15:33 UTC](https://meta.discourse.org/t/migrate-to-s3-failed-but-only-check-fails/394193 "2026-01-23T15:33:13Z")
**Posts on this page:** 1
**Page:** 1

<div class="post-metadata">

### Author: ![RGJ](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/rgj/32/523185_2.png) [@RGJ](https://meta.discourse.org/u/RGJ)
#### Post date: [23 januari 2026 om 15:33 UTC](https://meta.discourse.org/t/migrate-to-s3-failed-but-only-check-fails/394193/1 "2026-01-23T15:33:13Z")

</div>

Over the years we have seen many, many issues with migration to s3, including implicit migrations when restoring a backup.

```plaintext
EXCEPTION: 8 posts are not remapped to new S3 upload URL. S3 migration failed for db 'default'.

```

Some of the many examples:

- [False positives on "posts are not remapped to new S3 upload URL"](https://meta.discourse.org/t/false-positives-on-posts-are-not-remapped-to-new-s3-upload-url/370290/)
- [Restore process cancelled at migrating uploads to S3 step](https://meta.discourse.org/t/restore-process-cancelled-at-migrating-uploads-to-s3-step/123233/)
- [Restore to New Host](https://meta.discourse.org/t/restore-to-new-host/219515/)

Today, I encountered another one of these and since it was Friday I decided to dive into it instead of just commenting out the check.

So we have this situation:

- we’re on multisite
- let’s assume `dbname` as the database name for this example
- we have S3\_CDN\_URL set
- we do not have CDN\_URL set

This is what happens in `/lib/file_store/to_s3_migration.rb`

[First](https://github.com/discourse/discourse/blob/a96ebac814c21c359200e170daa421007368da8b/lib/file_store/to_s3_migration.rb#L190) the prefix is decided

`prefix = @migrate_to_multisite ? "uploads/#{@current_db}/original/" : "original/"`

Then the files are uploaded to s3 and [then](https://github.com/discourse/discourse/blob/a96ebac814c21c359200e170daa421007368da8b/lib/file_store/to_s3_migration.rb#L288-L326) the remap is done which is basically this and some variations

```plaintext
        from = "/uploads/#{@current_db}/original/"
        to = "#{SiteSetting.Upload.s3_base_url}/#{prefix}"

```

So in multisite this will remap

- from `/uploads/dbname/original/`
- to `https://bucket-location-url.com/uploads/dbname/original/`

and then [finally](https://github.com/discourse/discourse/blob/a96ebac814c21c359200e170daa421007368da8b/lib/file_store/to_s3_migration.rb#L77-L78) the check is done

```plaintext
      cdn_path = SiteSetting.cdn_path("/uploads/#{@current_db}/original").sub(/https?:/, "")
      count = Post.where("cooked LIKE '%#{cdn_path}%'").count
      if count > 0
        error_message = "#{count} posts are not remapped to new S3 upload URL. #{failure_message}"
        raise_or_log(error_message, should_raise)
        success = false
      end

```

Now `SiteSetting.cdn_path` comes from `lib/global_path.rb` and looks like this

```plaintext
  def cdn_path(p)
    GlobalSetting.cdn_url.blank? ? p : "#{GlobalSetting.cdn_url}#{path(p)}"
  end

```

Sooooooo if we do have an S3 CDN but not a regular CDN then `SiteSetting.cdn_path("/uploads/#{@current_db}/original")` will be simply `/uploads/dbname/original`

and, per our remap, the new paths are `https://bucket-location-url.com/uploads/dbname/original/`

That means

1. `cdn_path` is a substring of the new destination path
2. the `Post.where("cooked LIKE '%#{cdn_path}%'").count` will thus always find posts
3. it will cry wolf and bail out 😱 😱 😱
