How It Works Features Pricing Blog Error Guides
Log In Start Free Trial
Rails · Ruby

Fix ActiveRecord::StatementInvalid: PG::UndefinedColumn: ERROR: column posts.comments_count does not exist in Rails

This error occurs when you enable counter_cache on an association but the parent table does not have the required counter column. Rails expects a column named associations_count on the parent model table. Add the column with a migration and optionally reset counters for existing records using reset_counters.

Reading the Stack Trace

ActiveRecord::StatementInvalid (PG::UndefinedColumn: ERROR: column "posts.comments_count" does not exist): activerecord (7.1.3) lib/active_record/connection_adapters/postgresql_adapter.rb:672:in `exec_params' activerecord (7.1.3) lib/active_record/counter_cache.rb:38:in `update_counters' activerecord (7.1.3) lib/active_record/associations/belongs_to_association.rb:120:in `update_counters' app/models/comment.rb:3:in `<class:Comment>' app/controllers/comments_controller.rb:10:in `create'

Here's what each line means:

Common Causes

1. Missing counter cache column

The model declares counter_cache but the database table lacks the column.

class Comment < ApplicationRecord
  belongs_to :post, counter_cache: true
  # But posts table does not have a comments_count column
end

2. Wrong column name for counter cache

The counter cache column exists but with a different name than Rails expects.

# Migration added 'comment_count' but Rails expects 'comments_count'
class Comment < ApplicationRecord
  belongs_to :post, counter_cache: true  # Expects comments_count
end

3. Counter cache out of sync

The counter cache column exists but its values do not match the actual association count.

# posts.comments_count is 0 but the post actually has 15 comments
# This happens after bulk imports or direct SQL updates

The Fix

Add the comments_count column to the posts table with a default of 0. After running the migration, reset counters for existing records so the cached counts match the actual number of comments.

Before (broken)
class Comment < ApplicationRecord
  belongs_to :post, counter_cache: true
end
# Missing migration for comments_count column
After (fixed)
# 1. Generate migration:
# rails generate migration AddCommentsCountToPosts comments_count:integer

class AddCommentsCountToPosts < ActiveRecord::Migration[7.1]
  def change
    add_column :posts, :comments_count, :integer, default: 0, null: false
  end
end

# 2. Reset counters for existing records:
# rails runner 'Post.find_each { |p| Post.reset_counters(p.id, :comments) }'

Testing the Fix

require 'rails_helper'

RSpec.describe Comment, type: :model do
  describe 'counter cache' do
    let(:post) { create(:post) }

    it 'increments comments_count when a comment is created' do
      expect { create(:comment, post: post) }.to change {
        post.reload.comments_count
      }.by(1)
    end

    it 'decrements comments_count when a comment is destroyed' do
      comment = create(:comment, post: post)
      expect { comment.destroy }.to change {
        post.reload.comments_count
      }.by(-1)
    end
  end
end

Run your tests:

bundle exec rspec spec/models/comment_spec.rb

Pushing Through CI/CD

git checkout -b fix/rails-counter-cache,git add db/migrate/ app/models/comment.rb,git commit -m "fix: add comments_count column for counter cache",git push origin fix/rails-counter-cache

Your CI config should look something like this:

name: CI
on:
  pull_request:
    branches: [main]
jobs:
  test:
    runs-on: ubuntu-latest
    services:
      postgres:
        image: postgres:16
        env:
          POSTGRES_PASSWORD: postgres
        ports: ['5432:5432']
    steps:
      - uses: actions/checkout@v4
      - uses: ruby/setup-ruby@v1
        with:
          ruby-version: '3.3'
          bundler-cache: true
      - run: bin/rails db:setup
      - run: bundle exec rspec

The Full Manual Process: 18 Steps

Here's every step you just went through to fix this one bug:

  1. Notice the error alert or see it in your monitoring tool
  2. Open the error dashboard and read the stack trace
  3. Identify the file and line number from the stack trace
  4. Open your IDE and navigate to the file
  5. Read the surrounding code to understand context
  6. Reproduce the error locally
  7. Identify the root cause
  8. Write the fix
  9. Run the test suite locally
  10. Fix any failing tests
  11. Write new tests covering the edge case
  12. Run the full test suite again
  13. Create a new git branch
  14. Commit and push your changes
  15. Open a pull request
  16. Wait for code review
  17. Merge and deploy to production
  18. Monitor production to confirm the error is resolved

Total time: 30-60 minutes. For one bug.

Or Let bugstack Fix It in Under 2 minutes

Every step above? bugstack does it automatically.

Step 1: Install the SDK

gem install bugstack

Step 2: Initialize

require 'bugstack'

Bugstack.init(api_key: ENV['BUGSTACK_API_KEY'])

Step 3: There is no step 3.

bugstack handles everything from here:

  1. Captures the stack trace and request context
  2. Pulls the relevant source files from your GitHub repo
  3. Analyzes the error and understands the code context
  4. Generates a minimal, verified fix
  5. Runs your existing test suite
  6. Pushes through your CI/CD pipeline
  7. Deploys to production (or opens a PR for review)

Time from error to fix deployed: Under 2 minutes.

Human involvement: zero.

Try bugstack Free →

No credit card. 5-minute setup. Cancel anytime.

Deploying the Fix (Manual Path)

  1. Create and run the migration to add the counter column.
  2. Reset counters for all existing records.
  3. Run model specs verifying counter increments and decrements.
  4. Open a pull request.
  5. Merge, run migration on production, and reset counters.

Frequently Asked Questions

BugStack runs the fix through your existing test suite, generates additional edge-case tests, and validates that no other components are affected before marking it safe to deploy.

BugStack never pushes directly to production. Every fix goes through a pull request with full CI checks, so your team can review it before merging.

Use counter cache when you display counts frequently and the association does not change often. Use count queries for rarely displayed counts on frequently changing associations.

Use Post.reset_counters(post_id, :comments) for individual records or Post.find_each { |p| Post.reset_counters(p.id, :comments) } for all records.