ruby-grape/grape · error · ArgumentError

Status code must be Integer or Symbol.

Error message

Status code must be Integer or Symbol.

What it means

The `status` helper inside a Grape route stores the HTTP status code for the response. It only accepts an Integer (e.g. 201) or a Symbol resolvable by Rack::Utils.status_code (e.g. :created); Rack requires a concrete numeric code at render time. Any other class - most commonly a String like '201' or 'Created' - raises 'Status code must be Integer or Symbol.'.

Source

Thrown at lib/grape/dsl/inside_route.rb:69

          status 302
          body_message ||= "This resource has been moved temporarily to #{url}."
        end
        header 'Location', url
        content_type 'text/plain'
        body body_message
      end

      # Set or retrieve the HTTP status code.
      #
      # @param status [Integer] The HTTP Status Code to return for this request.
      def status(status = nil)
        return @status || default_status if status.nil?

        case status
        when Symbol, Integer
          @status = Rack::Utils.status_code(status)
        else
          raise ArgumentError, 'Status code must be Integer or Symbol.'
        end
      end

      # Set response content-type
      def content_type(val = nil)
        return header(Rack::CONTENT_TYPE, val) if val

        header[Rack::CONTENT_TYPE]
      end

      # Allows you to define the response body as something other than the
      # return value.
      #
      # @example
      #   get '/body' do
      #     body "Body"
      #     "Not the Body"
      #   end

View on GitHub (pinned to 22d7975629)

Solutions

  1. Pass an Integer: `status 201`.
  2. Pass a Rack-resolvable Symbol: `status :created`.
  3. Normalize dynamic values before calling status: `status Integer(v)` for numeric strings or `status v.to_sym` for names, guarding with a whitelist.

Example fix

# before
status ENV.fetch('OVERRIDE_STATUS', '201') # String raises

# after
status Integer(ENV.fetch('OVERRIDE_STATUS', '201'))

# or a name-based value
status ENV.fetch('OVERRIDE_STATUS_NAME', 'created').to_sym
Defensive patterns

Strategy: type-guard

Validate before calling

def normalize_status(value)
  case value
  when Integer then value
  when Symbol then value
  when String then value.match?(/\A\d+\z/) ? Integer(value) : value.to_sym
  else raise ArgumentError, "unusable status: #{value.inspect}"
  end
end

status normalize_status(ENV.fetch('OVERRIDE_STATUS', '201'))

Type guard

def valid_status?(value) = value.is_a?(Integer) || (value.is_a?(Symbol) && Rack::Utils::HTTP_STATUS_CODES.key?(Rack::Utils.status_code(value)))

Prevention

When it happens

Trigger: `status '201'` or `status 'Created'` (String read from config, an env var, or a client-supplied value). `status :not_found` works, but `status 'not_found'` (string symbol) raises. Passing an object whose to_s is a status, e.g. `status some_model.state`.

Common situations: Forwarding an upstream service's status read as a String from JSON/HTTP. Driving status from configuration or ENV variables without conversion. Copying examples from Rack/Sinatra-style code that use strings.

Related errors


AI-assisted analysis of ruby-grape/grape@22d7975629 (2026-08-21). Data as JSON: /api/errors/bfbe077bd146eb03. Report an issue: GitHub.