Skip to content

A 503 response raises ArgumentError instead of ServiceUnavailableError #230

Description

@davedufresne

Summary

Every HTTP error status is turned into an exception by Square::Errors::ResponseError.subclass_for_code(code) and raised as error_class.new(response.body, code: code) (e.g. lib/square/payments/client.rb). All the classes that method returns inherit ResponseError, whose initializer accepts (msg, code:) — except ServiceUnavailableError, which inherits plain ApiError (< StandardError):

# lib/square/errors/server_error.rb
module Square
  module Errors
    class ServerError < ResponseError
    end

    class ServiceUnavailableError < ApiError
    end
  end
end

So when the API answers 503, the SDK crashes while constructing its own exception, and callers get an ArgumentError instead of the ServiceUnavailableError they are told to rescue:

require "square"

Square::Errors::ResponseError.subclass_for_code(503).new("body", code: 503)
# => ArgumentError: wrong number of arguments (given 2, expected 0..1)

In practice, any endpoint call during a Square 503 raises:

ArgumentError: wrong number of arguments (given 2, expected 0..1)
  .../square.rb-45.0.2.20260122/lib/square/payments/client.rb:130:in 'Exception#initialize'
  .../square.rb-45.0.2.20260122/lib/square/payments/client.rb:130:in 'Square::Payments::Client#create'

This is painful for payment flows specifically: a 503 on payments.create is an ambiguous outcome that client code needs to catch and handle deliberately, and an ArgumentError escaping from inside the SDK is easy to misclassify as an application bug.

Affected versions

Reproduced on square.rb 45.0.2.20260122; the hierarchy is unchanged on current main (lib/square/errors/server_error.rb). Introduced with the error-class hierarchy from #196.

Suggested fix

Make ServiceUnavailableError inherit ResponseError (or give it a (msg, code:) initializer) so subclass_for_code's uniform new(body, code:) call works for 503 like it does for every other status. Since the SDK is Fern-generated, presumably a generator-side change.

Workaround

We currently patch the signature in an initializer:

module Square
  module Errors
    class ServiceUnavailableError
      attr_reader :code

      def initialize(msg = nil, code: nil)
        @code = code
        super(msg)
      end
    end
  end
end

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions