ruby/ruby · error · BadURIError

relative URI: #{oth}

Error message

relative URI: #{oth}

What it means

Same route_from0 code path as its sibling guard, but here the argument (the starting/base URI) is relative. URI::Generic#route_from requires the base to be absolute so scheme, authority and hierarchy can be compared with the destination.

Source

Thrown at lib/uri/generic.rb:1225

        else
          return tmp
        end
      end

      return '../' * src_path.size + tmp
    end
    private :route_from_path
    # :startdoc:

    # :stopdoc:
    def route_from0(oth)
      oth = parser.__send__(:convert_to_uri, oth)
      if self.relative?
        raise BadURIError,
          "relative URI: #{self}"
      end
      if oth.relative?
        raise BadURIError,
          "relative URI: #{oth}"
      end

      if self.scheme != oth.scheme
        return self, self.dup
      end
      rel = URI::Generic.new(nil, # it is relative URI
                             self.userinfo, self.host, self.port,
                             nil, self.path, self.opaque,
                             self.query, self.fragment, parser)

      if rel.userinfo != oth.userinfo ||
          rel.host.to_s.downcase != oth.host.to_s.downcase ||
          rel.port != oth.port

        if self.userinfo.nil? && self.host.nil?
          return self, self.dup
        end

View on GitHub (pinned to 0e5b888e1c)

Solutions

  1. Absolutize the base: URI.join(origin, base_path) before calling route_from
  2. Double-check argument order: a.route_from(b) means 'route from b to a'
  3. Guard both operands with #absolute? before computing a route

Example fix

# before
dest = URI('http://example.com/docs/a')
rel  = dest.route_from('/docs/index.html')
# BadURIError: relative URI: /docs/index.html

# after
origin = URI('http://example.com/')
rel = dest.route_from(origin + 'docs/index.html')   # => "a"
Defensive patterns

Strategy: validation

Validate before calling

def route(dest, base, origin)
  o    = URI(origin)
  dest = URI.join(o, dest.to_s)
  base = URI.join(o, base.to_s)
  dest.route_from(base)
end

Type guard

def absolute?(u)
  URI.parse(u.to_s).absolute?
rescue URI::InvalidURIError
  false
end

Try / catch

dest.route_from(base)
rescue URI::BadURIError => e
  raise ArgumentError, "route_from needs absolute base (#{base}) and dest (#{dest}): #{e.message}"
end

Prevention

When it happens

Trigger: URI('http://example.com/a/b').route_from(URI.parse('c/d')); absolute.route_from('/other/path') with a path-only base; 'x'.route_to(dest) — route_to calls dest.route_from(self) and self is the relative one.

Common situations: Passing request.path as the base when computing relative links; config-supplied base stored as a bare path; accidentally swapping argument order between route_from and route_to.

Related errors


AI-assisted analysis of ruby/ruby@0e5b888e1c (2026-08-21). Data as JSON: /api/errors/4a2241d86a6496f5. Report an issue: GitHub.