carrierwaveuploader/carrierwave · error · CarrierWave::InvalidParameter

invalid filename

Error message

invalid filename

What it means

Raised as CarrierWave::InvalidParameter by Uploader::Cache#original_filename= when the filename portion contains characters matched by CarrierWave::SanitizedFile.sanitize_regexp (/[^[:word:]\.\-+]/) — i.e. anything other than letters, digits, underscore, dot, dash, or plus. This guards the cache path against filenames that could escape or corrupt the cache directory (slashes, '../', spaces, parentheses, null bytes).

Source

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

      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
end # CarrierWave

View on GitHub (pinned to b5f0abe10e)

Solutions

  1. Validate/sanitize the filename before retrieve: filename.gsub(/[^[:word:]\.\-+]/, '_')
  2. Pass through the untouched uploader-provided cache_name instead of reconstructing it client-side
  3. Rescue CarrierWave::InvalidParameter and reject the request as malformed

Example fix

# before
cache_name = "#{params[:cache_id]}/#{params[:filename]}"
uploader.retrieve_from_cache!(cache_name) # 'my photo.jpg' -> InvalidParameter

# after
safe_name = params[:filename].to_s.gsub(/[^[:word:]\.\-+]/, '_')
uploader.retrieve_from_cache!("#{params[:cache_id]}/#{safe_name}")
Defensive patterns

Strategy: validation

Validate before calling

UNSAFE_FILENAME = /[^[:word:]\.\-+]/

def sanitize_upload_filename(name)
  File.basename(name.to_s).gsub(UNSAFE_FILENAME, '_')
end

cache_name = "#{params[:cache_id]}/#{sanitize_upload_filename(params[:filename])}"

Type guard

def safe_filename?(name)
  !name.to_s.match?(CarrierWave::SanitizedFile.sanitize_regexp)
end

Try / catch

begin
  uploader.retrieve_from_cache!(cache_name)
rescue CarrierWave::InvalidParameter
  head :bad_request # treat tampered cache field as malformed input
end

Prevention

When it happens

Trigger: retrieve_from_cache!(cache_name) where the part after the slash contains unsafe characters (e.g. '../evil.php', 'my photo (1).jpg'), or any code assigning uploader.original_filename = with a raw user-supplied name. The normal cache! flow is safe because it wraps files in SanitizedFile which replaces such characters before this setter runs.

Common situations: Tampered hidden cache field replaying a filename with traversal characters; client code rebuilding cache names from user input; middleware or JS upload widgets that re-encode the filename and introduce characters outside the allowed set.

Related errors


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