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
Summary
Every HTTP error status is turned into an exception by
Square::Errors::ResponseError.subclass_for_code(code)and raised aserror_class.new(response.body, code: code)(e.g.lib/square/payments/client.rb). All the classes that method returns inheritResponseError, whose initializer accepts(msg, code:)— exceptServiceUnavailableError, which inherits plainApiError(< StandardError):So when the API answers 503, the SDK crashes while constructing its own exception, and callers get an
ArgumentErrorinstead of theServiceUnavailableErrorthey are told to rescue:In practice, any endpoint call during a Square 503 raises:
This is painful for payment flows specifically: a 503 on
payments.createis an ambiguous outcome that client code needs to catch and handle deliberately, and anArgumentErrorescaping from inside the SDK is easy to misclassify as an application bug.Affected versions
Reproduced on
square.rb45.0.2.20260122; the hierarchy is unchanged on currentmain(lib/square/errors/server_error.rb). Introduced with the error-class hierarchy from #196.Suggested fix
Make
ServiceUnavailableErrorinheritResponseError(or give it a(msg, code:)initializer) sosubclass_for_code's uniformnew(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: