carrierwaveuploader/carrierwave · error · CarrierWave::InvalidParameter

invalid cache id

Error message

invalid cache id

What it means

Raised as CarrierWave::InvalidParameter by Uploader::Cache#cache_id= when the cache id portion of a cache name does not match /\A(-)?\d+-\d+(-\d{4})?-\d{4}\z/. CarrierWave cache names look like 'TIMEINT-PID-COUNTER-RND/filename.ext' (the regex also tolerates the older 3-part format); anything else is rejected before the cache storage is touched, as an anti-tampering measure on the hidden cache form field.

Source

Thrown at lib/carrierwave/uploader/cache.rb:204

        File.join(*[cache_dir, @cache_id, for_file].compact)
      end

    protected

      attr_reader :cache_id

    private

      def workfile_path(for_file=original_filename)
        File.join(CarrierWave.tmp_path, @cache_id, version_name.to_s, for_file)
      end

      attr_reader :original_filename

      def cache_id=(cache_id)
        # Earlier version used 3 part cache_id. Thus we should allow for
        # the cache_id to have both 3 part and 4 part formats.
        raise CarrierWave::InvalidParameter, "invalid cache id" unless cache_id =~ /\A(-)?[\d]+\-[\d]+(\-[\d]{4})?\-[\d]{4}\z/
        @cache_id = cache_id
      end

      def original_filename=(filename)
        raise CarrierWave::InvalidParameter, "invalid filename" if filename =~ CarrierWave::SanitizedFile.sanitize_regexp
        @original_filename = filename
      end

      def cache_storage
        @cache_storage ||= (self.class.cache_storage || self.class.storage).new(self)
      end

      # We can override the full_original_filename method in other modules
      def full_original_filename
        forcing_extension(original_filename)
      end
    end # Cache
  end # Uploader

View on GitHub (pinned to b5f0abe10e)

Solutions

  1. Validate the cache param against the expected pattern before calling retrieve_from_cache!
  2. Check the form: the hidden field must carry the exact uploader.cache_name value (use the provided form helpers so the round-trip is automatic)
  3. Rescue CarrierWave::InvalidParameter and treat it as a fresh upload instead of crashing
  4. Regenerate the cache value by re-uploading if the id came from a different CarrierWave version

Example fix

# before
uploader.retrieve_from_cache!(params[:image_cache]) # tampered value -> InvalidParameter

# after
CACHE_ID = /\A-?\d+-\d+(-\d{4})?-\d{4}\z/.freeze
cache_name = params[:image_cache].to_s
if cache_name.split('/', 2).first =~ CACHE_ID
  uploader.retrieve_from_cache!(cache_name)
else
  uploader.cache!(params[:image]) # fall back to the fresh upload
end
Defensive patterns

Strategy: validation

Validate before calling

CACHE_NAME = /\A(-?\d+-\d+(-\d{4})?-\d{4})\/[^\/]+\z/.freeze

def valid_cache_name?(name)
  name.to_s.match?(CACHE_NAME)
end

uploader.retrieve_from_cache!(cache_name) if valid_cache_name?(params[:image_cache])

Type guard

def cache_param?(value)
  value.is_a?(String) && value.match?(CACHE_NAME)
end

Try / catch

begin
  uploader.retrieve_from_cache!(params[:image_cache])
rescue CarrierWave::InvalidParameter
  uploader.cache!(params[:image]) # treat as a fresh upload
end

Prevention

When it happens

Trigger: uploader.retrieve_from_cache!(params[:image_cache]) where the submitted value was truncated, edited, or is a different field entirely: passing the filename alone, passing the full 'cache_id/filename' with a malformed id segment (letters where digits belong, missing parts), or an attacker probing the cache param.

Common situations: Re-rendering a form after validation errors where the hidden field name no longer matches params (e.g. nested attributes, form renaming); copy-pasting cache values between environments; manual curl posts omitting the id pieces; cache ids generated by a newer/older CarrierWave than the one re-reading them.

Related errors


AI-assisted analysis of carrierwaveuploader/carrierwave@b5f0abe10e (2026-08-21). Data as JSON: /api/errors/80e74ceab2e3708d. Report an issue: GitHub.