As a Rails developer working with PostgreSQL, I ran into an issue that had me scratching attributes in ruby my head for a bit. Every time I tried to save a string that contained a null byte ("\u0000"
), PostgreSQL would throw an error like this:
ActiveRecord::StatementInvalid
PG::UntranslatableCharacter: ERROR: unsupported Unicode escape sequence
This wasn’t just annoying it was breaking real functionality in production. So, I decided to write a solution that automatically removes null bytes from every string attribute in all of my ActiveRecord models ruby without manually editing each one.
Let me walk you through the journey, the first bug I hit, how I fixed it, and how I made it scalable and testable.
First Error in the Code
My first attempt looked something like this:
def remove_null_bytes
my_field.delete!("\u0000")
end
The Problem
The issue here is that my_field
isn’t defined anywhere. It’s just a placeholder a hardcoded assumption about what attribute I wanted to clean. If that attribute using ruby doesn’t exist or is nil
, this method will crash.
The Fix
Instead of hardcoding, I needed a way to loop through all string attributes dynamically and clean them one by one. That led me to think in terms of Rails Concerns
Explanation of the Original Code
Here’s my initial model:
class MyModel < ApplicationRecord
before_save :remove_null_bytes
private
def remove_null_bytes
my_field.delete!("\u0000")
end
end
What It Does:
before_save :remove_null_bytes
: This hook is triggered before saving the record.remove_null_bytes
: Attempts to remove null bytes frommy_field
.
But There Are Limitations:
- It only targets one field (
my_field
). - Doesn’t scale to other models or attributes.
- Crashes if the attribute is
nil
.
Clearly, this wasn’t going to work for the whole application
Improved & Practical Version for All Models
To solve this the right way, I wrote a Rails concern that works for all models. It automatically strips null bytes from every string column, and it does so without touching binary data (which might legitimately contain null bytes).
Here’s the Concern:
# app/models/concerns/remove_null_bytes.rb
module RemoveNullBytes
extend ActiveSupport::Concern
included do
before_save :sanitize_string_attributes
end
private
def sanitize_string_attributes
self.class.columns.each do |column|
next unless column.type == :string
value = read_attribute(column.name)
next unless value.is_a?(String)
cleaned = value.delete("\u0000")
write_attribute(column.name, cleaned) if cleaned != value
end
end
end
And I included it in ApplicationRecord
like this:
# app/models/application_record.rb
class ApplicationRecord < ActiveRecord::Base
self.abstract_class = true
include RemoveNullBytes
end
This now applies to every model in my application, ensuring null bytes are removed only from string attributes using ruby, leaving binary and other data types untouched.
Extra Practice Functionalities You Can Add
If you want to take this further, here are some real-world enhancements you can include:
Logging when null bytes are removed
Rails.logger.info "Removed null byte from #{self.class.name}##{column.name}"
Add a dry-run mode for testing
Use an environment variable to toggle the behavior without modifying data:
next if ENV['NULL_BYTE_SANITIZER'] == 'off'
Write a unit test for the concern
describe RemoveNullBytes do
it "removes null bytes from string attributes" do
model = MyModel.create(name: "test\u0000ing")
expect(model.name).to eq("testing")
end
end
Add a metrics counter (if using Datadog or Prometheus)
MyApp::Metrics.increment('null_bytes.cleaned') if cleaned != value
Final Thoughts
This small but powerful concern made my Rails app more stable and production-ready. It fixed a PostgreSQL compatibility issue and did so in a clean, DRY, and scalable way.
If you’re running into similar issues — or just want to sanitize your string attributes before saving I highly recommend adopting this pattern. It’s a one-time setup that adds a silent layer of protection across your app.