ruby/ruby · error · ArgumentError
uri scheme is invalid: #{uri.scheme.inspect}
Error message
uri scheme is invalid: #{uri.scheme.inspect} What it means
Gem::RemoteFetcher#fetch_path dispatches on the URI scheme to a concrete fetcher: http/https map to fetch_http, s3 to fetch_s3, and file to fetch_file. Any other scheme - including nil, which is what a scheme-less string like "/tmp/specs.4.8.gz" parses to - raises ArgumentError with the inspected scheme. The error is raised by Hash#fetch's default block, so it fires before any download attempt.
Source
Thrown at lib/rubygems/remote_fetcher.rb:259
error_detail = custom_error || response.message
raise FetchError.new("Bad response #{error_detail} #{response.code}", uri)
end
end
alias_method :fetch_https, :fetch_http
##
# Downloads +uri+ and returns it as a String.
def fetch_path(uri, mtime = nil, head = false)
uri = Gem::Uri.new uri
method = {
"http" => "fetch_http",
"https" => "fetch_http",
"s3" => "fetch_s3",
"file" => "fetch_file",
}.fetch(uri.scheme) { raise ArgumentError, "uri scheme is invalid: #{uri.scheme.inspect}" }
data = send method, uri, mtime, head
if data && !head && uri.to_s.end_with?(".gz")
begin
data = Gem::Util.gunzip data
rescue Zlib::GzipFile::Error
raise FetchError.new("server did not return a valid file", uri)
end
end
data
rescue Gem::Timeout::Error, IOError, SocketError, SystemCallError,
*(OpenSSL::SSL::SSLError if Gem::HAVE_OPENSSL) => e
raise FetchError.new("#{e.class}: #{e}", uri)
end
def fetch_s3(uri, mtime = nil, head = false)View on GitHub (pinned to 0e5b888e1c)
Solutions
- Pass a proper URI: use http:// or https:// for remote files
- Prefix local paths with file:// (e.g. URI('file:///tmp/specs.4.8.gz')) so the scheme is 'file'
- Audit `gem sources -l` and ~/.gemrc for entries whose scheme is not http/https and remove them with `gem sources -r`
- For s3, keep the s3:// scheme and configure credentials via the standard s3 env vars RubyGems honors
Example fix
# before
fetcher.fetch_path("/var/gem_mirror/specs.4.8.gz")
# after
fetcher.fetch_path(URI("file:///var/gem_mirror/specs.4.8.gz")) Defensive patterns
Strategy: validation
Validate before calling
uri = Gem::Uri.new(source)
raise ArgumentError, "scheme must be http/https/s3/file, got #{uri.scheme.inspect}" unless %w[http https s3 file].include?(uri.scheme)
fetcher.fetch_path(uri) Try / catch
begin
fetcher.fetch_path(uri)
rescue ArgumentError => e
# distinguish scheme errors (message contains 'uri scheme is invalid') from other argument errors
raise if !e.message.include?('uri scheme is invalid')
warn "skipping unsupported source #{uri}" and nil
end Prevention
- Always pass URI objects or strings with an explicit scheme; never bare filesystem paths
- Use file:// prefixed URIs for local mirror files
- Centralize source URL construction so scheme validation happens in one place
When it happens
Trigger: Gem::RemoteFetcher.fetcher.fetch_path('ftp://host/file') or fetch_path('/local/path/specs.4.8.gz') (nil scheme). Also hit indirectly by gem commands (gem fetch, gem spec, dependency resolution) when a configured source or spec URI carries a non-http/https/s3/file scheme.
Common situations: Passing a plain filesystem path string where a file:// URI is required; a source left in `gem sources -l` with a bad protocol; tooling that builds URIs by string concatenation and drops the scheme; using fetch_path on git: or ssh: URIs.
Related errors
- unsupported URI scheme #{source_uri.scheme}
- can't open %s. give up.\n
- bad component(expected scheme component): #{v}
- bad URI (absolute but no scheme): #{uri}
- bad URI (absolute but no path): #{uri}
AI-assisted analysis of ruby/ruby@0e5b888e1c (2026-08-21).
Data as JSON: /api/errors/f9b4afc0cb5b117e.
Report an issue: GitHub.