Ruby ensure Keyword
last modified October 18, 2026
This tutorial explains how to use Ruby's ensure keyword. The
ensure clause guarantees code execution regardless of exceptions.
The ensure keyword defines a block that always executes after a
begin block, whether an exception occurs or not. It's typically
used for cleanup operations.
ensure works with begin, rescue, and
else keywords. It runs after the begin body, after a
rescue block, or after the else clause. It runs even
if the code returns early or raises an exception.
Basic ensure Example
This example shows how ensure relates to rescue and
else. The else clause runs only when no exception was
raised, and ensure always runs last.
def operate(should_fail)
begin
puts "Performing operation"
raise "Error occurred" if should_fail
puts "Operation succeeded"
rescue => e
puts "Rescued: #{e.message}"
else
puts "No exception was raised"
ensure
puts "This always runs"
end
end
operate(false)
puts "---"
operate(true)
On success, the body runs, then else, then ensure.
On failure, rescue runs instead of else, and
ensure still runs. This makes ensure ideal for cleanup
tasks such as closing files or releasing resources.
File Handling with ensure
A common use of ensure is closing files. This example closes the
file on both the success and failure paths.
def write_file(should_fail)
file = File.open("data.txt", "w")
file.puts "Writing important data"
raise "Disk error" if should_fail
puts "Write completed"
rescue => e
puts "Error writing file: #{e.message}"
ensure
if file
file.close
puts "File closed successfully"
end
end
write_file(false)
puts "---"
write_file(true)
The file closes on both paths, preventing resource leaks. The if file
check avoids calling close when File.open itself
failed.
Database Connection with ensure
Database connections should always be properly closed. This example shows how
ensure guarantees connection cleanup. It requires the
sqlite3 gem.
require 'sqlite3'
begin
db = SQLite3::Database.new("test.db")
db.execute("CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY, name TEXT)")
db.execute("INSERT INTO users (name) VALUES ('John')")
rescue SQLite3::Exception => e
puts "Database error: #{e.message}"
ensure
if db
db.close
puts "Database connection closed"
end
end
The database connection closes whether the operations succeed or fail. This prevents connection leaks that could exhaust database resources.
ensure with Return Statements
ensure blocks run even when using return inside a
method. This example demonstrates this behavior.
def early_return
begin
puts "Starting method"
return "From the method body"
ensure
puts "Ensure block executed"
end
end
puts early_return
def overridden_return
begin
return "From the method body"
ensure
return "From the ensure block"
end
end
puts overridden_return
The first method shows that ensure runs before the method returns.
The second shows that a return inside ensure overrides
the method's result and can discard a pending exception. Use this behavior
sparingly.
Exceptions Raised in ensure
An exception raised inside ensure replaces the exception that was
propagating. Ruby keeps the original exception as the new exception's
cause.
begin
begin
raise "Original error"
ensure
raise "Error from ensure"
end
rescue => e
puts "Caught: #{e.message}"
puts "Cause: #{e.cause.message}"
end
The outer rescue catches the exception raised by
ensure, not the original one. The original error is still available
through cause.
Nested ensure Blocks
Ruby allows nested begin blocks with their own ensure
clauses. This example shows how they execute in order.
begin
puts "Outer begin"
begin
puts "Inner begin"
raise "Inner error"
ensure
puts "Inner ensure"
end
rescue => e
puts "Rescued: #{e.message}"
ensure
puts "Outer ensure"
end
Each ensure belongs to its own begin block. The inner
ensure runs before the outer rescue and
ensure, so cleanup happens from the inside out.
ensure with Threads
ensure blocks in threads execute when the thread terminates,
whether normally or due to an exception. This example demonstrates thread cleanup.
thread = Thread.new do
begin
puts "Thread working"
raise "Thread error"
ensure
puts "Thread cleanup"
end
end
thread.report_on_exception = false
begin
thread.join
rescue => e
puts "Main rescued: #{e.message}"
end
puts "Main program continues"
The thread's ensure block runs before the thread terminates.
Thread#join re-raises the thread's exception in the calling thread,
so the example rescues it. report_on_exception = false suppresses
Ruby's default warning about the terminated thread.
ensure in Method Definitions
Methods can use ensure without explicit begin blocks.
This example shows the shorthand syntax for method-level ensure.
def process_data(should_fail)
puts "Processing data"
raise "Processing error" if should_fail
rescue => e
puts "Error: #{e.message}"
ensure
puts "Cleanup resources"
end
process_data(false)
puts "---"
process_data(true)
The entire method body acts as an implicit begin block, so
rescue and ensure work without an explicit
begin. The ensure clause runs regardless of how the
method exits.
Source
Ruby Exception Handling Documentation
This tutorial covered Ruby's ensure keyword with practical examples
showing resource cleanup, return values, nested blocks, threads, and
method-level guarantees.
Author
List all Ruby tutorials.