Hacktoberfest 2026: the issues maintainers tagged for October, open and beginner-friendly. Browse Hacktoberfest issues

ActiveRecordRelations cannot generate an RBI for an Active Record model with a composite primary key

Closed
#2,723 0 comments 0 reactions 0 assignees View on GitHub

Maintainers usually reply within 3 days

@jgrau is already working on this.

Since Sep 28, 2026.

  • #2724 by @jgrau — open

Assessment

Difficulty
3/5
Estimated time
1-2 days
Newbie friendliness
76/100
Issue type
Bug
Clarity
Clearly specified
Activity status
Active
Tech stack
postgresql, rails, ruby
Domain
databases, tooling

Research direction

Start in lib/tapioca/dsl/compilers/active_record_relations.rb around line 801 and inspect ActiveRecordColumnTypeHelper#type_for in lib/tapioca/dsl/compilers/helpers/active_record_column_type_helper.rb around line 60. Run the supplied repro.rb with Rails 8 and PostgreSQL available, then verify generation succeeds for the composite key and produces typed tuple ids while preserving scalar primary-key behavior.

Written by the indexing model from the issue text.

Description

Problem

ActiveRecordRelations cannot generate an RBI for an Active Record model with a composite primary key. When generating ids, it passes
constant.primary_key—an Array—to ActiveRecordColumnTypeHelper#type_for, whose argument is typed as String.

Reproduced with Tapioca 0.19.2 and Rails 8. The same call is present in v0.20.0 and current main, though I have only run this reproduction against
0.19.2.

Minimal reproduction

With tapioca 0.19.2, Active Record 8, and the PostgreSQL adapter available, run bundle exec ruby repro.rb:

require "active_record"
require "active_record/connection_adapters/postgresql/oid/uuid"
require "tapioca/internal"
require "tapioca/dsl/compilers/active_record_relations"

class CompositeRecord < ActiveRecord::Base
  self.primary_key = %w[listing_id checkin_on checkout_on]

  def self.table_exists? = true

  def self.columns_hash
    %w[listing_id checkin_on checkout_on].to_h do |name|
      [name, Struct.new(:null).new(false)]
    end
  end

  def self.attribute_types
    {
      "listing_id" => ActiveRecord::ConnectionAdapters::PostgreSQL::OID::Uuid.new,
      "checkin_on" => ActiveRecord::Type::Date.new,
      "checkout_on" => ActiveRecord::Type::Date.new,
    }
  end
end

compiler = Tapioca::Dsl::Compilers::ActiveRecordRelations
pipeline = Tapioca::Dsl::Pipeline.new(
  requested_constants: [CompositeRecord],
  requested_compilers: [compiler],
)
root = RBI::Tree.new
compiler.new(pipeline, root, CompositeRecord).decorate

The metadata is supplied in memory to keep the reproduction database-free; it exercises the actual compiler.

Actual result

  TypeError: Parameter 'attribute_name': Expected type ::String,
  got type Array with value ["listing_id", "checkin_on", "checkout_on"]
  Caller: .../active_record_relations.rb:801
  Definition: .../active_record_column_type_helper.rb:60

Expected result

Generation succeeds, with ids returning typed tuples for these columns—for example, T::Array[[::String, ::Date, ::Date]].

ActiveRecordColumnTypeHelper#type_for("id") already delegates to its composite-aware id_type method. Could the Relations compiler use that path for ids,
while preserving scalar/nonstandard primary-key behavior?

Related earlier work: #1966 addresses composite find overloads; #2007 addresses composite column types.

Dominant language
Ruby
Stars
874
Forks
165
Avg merge
5d 2h
Merged PRs (30d)
12

Getting set up

  • No Dockerfile or Docker Compose file
  • Has a pull request template
  • No contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from Shopify/tapioca

All issues in Shopify/tapioca

Similar issues

More Ruby issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.