Skip to content
Innovation & Growth

Rails change keeps SQLite from masking the real error after a failed write

A small fix merged to Rails main shows why failure paths deserve as much care as new features

W
WebPulse Newsroom
AI-assisted · 4 min read
Share on X LinkedIn
Rails change keeps SQLite from masking the real error after a failed write
In brief
  • Rails main now preserves the original error when SQLite rolls back a transaction itself, for example after a full disk, instead of reporting a misleading one.
  • Failure paths decide how fast a team finds the real cause of an outage, so they deserve the same review as features.
  • Ask your team whether these changes are in the Rails version you run, and whether your tests cover a full disk.

The error you read is not always the error that happened

Most engineering news is about what software can do. The more useful question for a leader is what software says when it fails. During an outage, the first error message points the team toward a cause. If that message is wrong, hours go to the wrong place.

A change merged to Rails main this week addresses exactly this. It is one item in the weekly changelog published by the Ruby on Rails project on October 16. The lesson here is that the code that handles failure is part of the product, and it needs the same scrutiny as the code that handles success.

What the SQLite fix does

A database transaction is a group of changes that either all succeed or all get undone. Undoing them is called a rollback. SQLite can perform that rollback on its own after certain errors, such as a full disk.

According to the Rails project, the framework used to issue a second rollback anyway. SQLite replied that there was no transaction to roll back. That reply, "cannot rollback - no transaction is active", replaced the original failure. Rails also skipped record cleanup.

The change skips the redundant rollback. The original failure now stays attached as the cause of an ActiveRecord::StatementInvalid error. Rails also puts records back in their prior state. It then fires after_rollback callbacks, the hooks an application registers to react when a rollback occurs.

Picture the person investigating. Before the change, the error raised pointed at a rollback problem, not the full disk. After it, the original failure is still attached as the cause. Records are also left in a clean state, so the application does not carry half-undone work forward.

30
Contributors to the Rails codebase this past week
Source: Ruby on Rails, This Week in Rails (October 16, 2026)

A second fix about whose connection is whose

Another change deals with a similar kind of quiet confusion. Rails lets code choose which database role and shard to use through a block called connected_to. A shard is one slice of a larger database.

The Rails project reports that ActionController::Live, which streams responses, handed the request's connected_to settings to its streaming thread. Both threads therefore worked from the same record. Exiting a block in one thread could alter the shard and role that the other thread saw. Rails now makes a modified copy whenever a thread pushes or pops, so each thread keeps its own connection context.

The source does not describe any incident caused by this. It does show the same pattern: shared state that behaves well in normal use and misleads under particular conditions.

The speed changes come with limits

The same changelog lists several performance items. String#blank? now scans bytes for ASCII-only strings instead of running a regular expression. Collection partials in Action View render into one shared buffer when neither caching nor per-item layouts need separate output.

For Active Record, the author of one pull request reported results from the ruby-bench suite. Average time, excluding warm-up, fell from 199 ms to 170 ms. That pull request also lists a figure for Rails 8.1.4, which it puts at 134 ms.

199 ms to 170 ms
Active Record benchmark, average time before and after one change
Source: Ruby on Rails, This Week in Rails, PR author's ruby-bench run (October 16, 2026)

This is one author's benchmark, not an independent test. Another change, which limits which connection pools are consulted, comes with a note from its pull request: most applications won't see a difference. Treat the speed items as modest until you measure your own workload.

Who is affected

These changes are merged to Rails main. The source does not say which release will carry them. Teams on a released version should check release notes before assuming they have the fixes.

In WebPulse's September 2026 scan of the Tranco top 10,000 domains, a platform was detected on 2,491 sites. Rails was detected on 85 of them. The scan is a sample of detected sites, not a measure of the whole web.

The scan does not show which database a site uses or which Rails version it runs. It cannot say how many of those sites run SQLite or could meet this bug. It only shows that Rails is present among the detected sites.

85 (3.4%)
Rails among sites with a detected platform, Tranco top 10,000
Source: WebPulse scan of Tranco top 10,000 domains (September 2026)

Questions to put to your team

First, does any production system use SQLite, and do you know which Rails version it runs? Second, when a database write fails, does your logging show the original cause or only the last error raised?

Third, has anyone tested what happens when the disk fills? Fourth, if you stream responses with ActionController::Live and switch database roles or shards, have the owners reviewed the connected_to change?

A framework that reports the true cause of a failure saves time on the worst day. It is worth asking whether your own systems do the same.

Produced by the WebPulse Newsroom with AI assistance from the original reporting credited below, and checked against that source by our editorial review. How we use AI.
Original reporting: Ruby on Rails.

Compare frameworks in this analysis
React vs Rails
Share this insight