Skip to content


Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Retry 403s and Net::HTTPBadResponse errors.

My testing strongly suggests that when GitHub returns status code 403 the
request can be retried. This may be how they implement rate limiting. So, if
we get a 403 we simply repeat the request. We don't wait between requests
because there is not yet any evidence that it would benefit us. Hopefully,
once the rate limiting is documented, we can revisit this issue.

We also retry on Net::HTTPBadResponse exceptions. These are typically raised
when something between the client and the server clobbers the response, so
repeating the request is the most sensible approach.

We don't limit the number of retries which means this code could end up
looping forever. I'm loath to specify some arbitrary limit, however, without
documentation on what to expect. For example, in the case of 403 errors, my
testing reveals that sometimes we succeed after retrying twice, and other
times it may take nearly ten retries.
  • Loading branch information...
commit 6cae2e317b2e4301200b62654e57a0394c9f8b46 1 parent 7b3b8fc
@runpaint authored
Showing with 13 additions and 2 deletions.
  1. +13 −2 lib/octopi.rb
15 lib/octopi.rb
@@ -65,6 +65,7 @@ class Api
'json' => 'application/json',
'xml' => 'application/sml'
base_uri ""
attr_accessor :format, :login, :token, :trace_level, :read_only
@@ -141,7 +142,12 @@ def submit(path, params = {}, format = "yaml", &block)
query = login ? { :login => login, :token => token } : {}
- resp = yield(path, query.merge(params), format)
+ begin
+ resp = yield(path, query.merge(params), format)
+ rescue Net::HTTPBadResponse
+ raise RetryableAPIError
+ end
if @trace_level
case @trace_level
@@ -153,7 +159,7 @@ def submit(path, params = {}, format = "yaml", &block)
puts "===================="
+ raise RetryableAPIError, resp.code.to_i if RETRYABLE_STATUS.include? resp.code.to_i
raise APIError,
"GitHub returned status #{resp.code}" unless resp.code.to_i == 200
# FIXME: This fails for showing raw Git data because that call returns
@@ -169,9 +175,14 @@ def submit(path, params = {}, format = "yaml", &block)
def get(path, params = {}, format = "yaml")
trace "GET [#{format}]", "/#{format}#{path}", params
+ begin
submit(path, params, format) do |path, params, format|
self.class.get "/#{format}#{path}"
+ rescue RetryableAPIError => e
+ $stderr.puts e.message
+ retry
+ end
def trace(oper, url, params)
Please sign in to comment.
Something went wrong with that request. Please try again.