I’d like to propose a small enhancement to ActiveRecord’s uniqueness validation: when validation fails because a value is already taken, include the primary key (id) of the existing record in errors.details in addition to the conflicting value.
Rationale:
It makes error handling in APIs and UIs more precise — clients can reference the exact existing resource instead of only knowing the duplicate value.
It helps debugging (quickly point to the conflicting row).
Two example shapes for the extra metadata:
Option A — use id as an extra key inside the details hash:
It should not change the semantics of uniqueness validation itself — only the metadata attached to errors.details.
I believe this small change would be valuable for API authors and client apps. If the core team and community find this useful, I’m happy to prepare a prototype and open a pull request implementing the behavior (with tests and an opt-in flag).
Thanks for reading — curious to hear thoughts and potential pitfalls I might have missed.
I think this is a useful little enhancement, and it shouldn’t break any existing code.
I prefer the hash key name in Option B: existing_id.If the validation fails during an update, it wouldn’t be obvious whether id refers to the record that already includes the unique value or the record that was being updated.
You’d need to include a consumer of the value in your change. I don’t imagine adding an additional value that is not immediately used/consumed immediately by anything else in Rails would be accepted.
I think making the PR would expose the challenges to this, which is possibly a de-optimization of the uniqueness query. But do it!
It sounds useful, but at the same time if you only need the ID to know what’s the record with that value, wouldn’t it be sufficient to query for this exact value as you know there’s only one record with it?
Ex: if you get { name: [{ error: :taken, value: "bar" }] } can you query for Model.find_by!(name: “bar”)?
That said, we should consider composite primary keys when implementing this too.
it’s no longer straightforward to determine which record is causing the conflict. In my case, I have to repeat the same validator logic just to figure out the existing record.
If the validation error could include something like: