Strange recursive cache behavior in conjunction with generate_location

I realize I’ve been posting a lot here lately, please bear with me on this one.

I gave up on my idea to have some of my uploaders use S3, while others used the filesystem. I stripped everything back to the least complex setup I could manage, and yes, files are being uploaded to my S3 bucket. However, the cache contains several recursive ghost folders. I’m using Panic Transmit to look in the actual cache folder, and here’s what I see:


All right so far. When I look in the store folder, I see what I expect to see, given my generate_location setup (code follows).

  sources/ (the table name of the AR model)
    256/ (the id of this source)

But in the cache folder, it’s a horror show:

  asdfyasdfasdfndsfasdf,ext (the cache of that file that made it to storage)
  sources/sources/sources/sources/ (infinite recursion here, you never reach the bottom)
  uploads/ (no idea where this is coming from -- nothing defines it)
    sources/sources/sources/sources/ (again, infinite recursion)

Here’s my shrine.rb initializer:

# frozen_string_literal: true

require 'shrine'
require 'shrine/storage/s3'


s3_props = { public: true,
  bucket: 'oll-resources',
  region: 'us-east-2',
  access_key_id: 'REDACTED',
  secret_access_key: 'REDACTED' 

Shrine.storages = {
  cache: prefix: 'oll3/cache', **s3_props),
  store: prefix: 'oll3/store', **s3_props),

Here’s the uploader I am working with (and all the others have been commented out for good measure and debugging)

# frozen_string_literal: true

class MediaUploader < Shrine
  # plugin :derivatives
  plugin :remove_attachment
  plugin :metadata_attributes, filename: :name
  plugin :determine_mime_type
  # plugin :derivation_endpoint, secret_key: '4f6dac3e1357476f9d5375084133541bf1db665d8d826834f3e0a52ea9769fceec69195a6efd89eb5ed0a030e78f8e650b9544f1423d27d6dee92b6ffb4725b6'

  # derivation :thumb do |file, width, height, x, y, scale_x = nil, scale_y = nil|
#     magick = ImageProcessing::MiniMagick.source(file)
#     magick = magick.crop("#{width}x#{height}+#{x}+y#{y}")
#     magick = magick.resize_to_fit(scale_x.to_i, scale_y.to_i) if scale_x && scale_y
#   end
  def generate_location(io, record: nil, derivative: nil, **)
    return super unless record&.persisted?
    table  = record.class.table_name
    id     =
    file   = record.file_name


I hope this rings a bell for someone, because I am at my wits’ end with this.