---
title: "RR007 – Return Correct HTTP Status Codes"
impact: medium
impactDescription: "Returning 200 OK for errors silently breaks API clients, prevents correct retry logic, and hides failures in monitoring."
tags: [ruby, rails, api, http]
---

# RR007 – Return Correct HTTP Status Codes

## Rule

Always pass an explicit `status:` argument to `render json:`. Never return HTTP 200 for validation errors, authorization failures, or not-found resources.

## Wrong

```ruby
def create
  @user = User.new(user_params)
  if @user.save
    render json: @user                # ❌ no status — defaults to 200
  else
    render json: { errors: @user.errors }   # ❌ 200 on failure
  end
end

def show
  @user = User.find_by(id: params[:id])
  render json: @user   # ❌ returns 200 with null body if not found
end
```

## Correct

```ruby
def create
  @user = User.new(user_params)
  if @user.save
    render json: UserResource.new(@user), status: :created          # 201
  else
    render json: { errors: @user.errors.full_messages }, status: :unprocessable_entity  # 422
  end
end

def show
  @user = User.find(params[:id])    # raises ActiveRecord::RecordNotFound → rescued to 404
  render json: UserResource.new(@user), status: :ok
end

# In ApplicationController
rescue_from ActiveRecord::RecordNotFound, with: :not_found
rescue_from Pundit::NotAuthorizedError,   with: :forbidden

def not_found
  render json: { error: 'Not found' }, status: :not_found           # 404
end
def forbidden
  render json: { error: 'Forbidden' }, status: :forbidden           # 403
end
```

## Status code reference

| Scenario | Status |
|---|---|
| Create success | `201 :created` |
| Update/delete success | `200 :ok` |
| No content | `204 :no_content` |
| Validation failed | `422 :unprocessable_entity` |
| Not found | `404 :not_found` |
| Unauthorized (not logged in) | `401 :unauthorized` |
| Forbidden (not allowed) | `403 :forbidden` |

## Notes

- Use symbol form (`:created`) — it's self-documenting and immune to numeric typos.
- Centralize rescue_from in `ApplicationController` or a concern rather than per-action `rescue`.
