rubyguides

Kernel#caller

caller([start [, length]]) -> [string]

The caller method returns the execution stack as an array of strings. Each element represents a location in the call stack, showing file path, line number, and sometimes method name.

Basic Usage

def one
  caller
end

def two
  one
end

puts two.first
# => /path/to/file.rb:10:in `two'

Each frame in the returned array represents a point in the call chain. The first entry is the nearest caller, and each subsequent entry moves further up the stack toward the program’s entry point. This format makes it straightforward to walk from where you are back to where the execution started, which is why caller is so often the first tool a developer reaches for when tracing a method’s origin without a debugger.

Practical Examples

Debugging

def log_call
  puts "Called from:"
  caller(1).each { |loc| puts "  #{loc}" }
end

def a
  log_call
end

def b
  a
end

b

# Output:
# Called from:
#   /path/to/file.rb:15:in `b'

The caller(1) argument skips the helper method itself from the output so the trace points directly at the caller that matters. Without that skip, the first frame would always be log_call, which is rarely what you want to see. The optional start index gives you fine-grained control over which part of the stack to expose, letting you hide internal wrapper frames that would otherwise clutter diagnostic output.

Error Messages

def validate!(value)
  unless value
    raise ArgumentError, "Value required", caller(1)
  end
end

Passing caller(1) as the third argument to raise replaces the default backtrace with one that starts one frame higher than normal. This is a common pattern in library code where you want errors to point at the user’s call site instead of at the library’s internal validation methods. Without it, the backtrace would show validate! as the source, burying the actual caller.

Stack Depth

def depth
  caller.length
end

def level1
  depth
end

def level2
  level1
end

puts level2  # => 3

The caller.length property is a quick way to measure how deep you are in a call chain. Each method call pushes a new frame, so the length directly reflects the nesting level. This can be useful when you are debugging recursion or when you want to confirm that a particular code path is reached through the expected number of intermediate layers. Keep in mind that the count includes the current method too.

Filtering Stack

# Skip first few frames
def trace
  caller(3)  # Skip 3 frames
end

Raise without trace

When you pass caller(1) as the second argument to raise, Ruby uses that backtrace instead of constructing one from the current frame. This is called stripping a level from the backtrace, and the effect is that the error appears to originate from your caller instead of from the helper method that actually calls raise. Library authors use this technique so users see their own code in the stack trace instead of a trail of internal helper frames that obscure the real source of the problem.

# Useful for libraries to show user code in errors
def my_raise(msg)
  raise msg, caller(1)
end

Using caller(1) inside a custom raise wrapper lets a library produce errors that look native. The user sees their own code in the backtrace instead of the library’s internal helper method. This small adjustment makes error messages feel more natural and reduces the mental distance between the stack trace and the code the developer needs to fix.

Format

def show_caller
  caller(0).each_with_index do |c, i|
    puts "#{i}: #{c}"
  end
end

# Output format:
# 0: /path/file.rb:10:in `method_name'
# 1: /path/file.rb:20:in `another_method'

Practical library usage

Logging

Logging helpers often sit inside shared modules that are called from many places across an application. Without caller context, every log line would point back to the helper itself, which tells you nothing about which code path triggered the message. By capturing caller(1).first and prepending it to each line, you turn a generic log entry into one that carries its own provenance.

def debug(message)
  location = caller(1).first
  puts "[DEBUG] #{Time.now} #{location}: #{message}"
end

Embedding the caller location directly into a log line gives each message automatic provenance. When you scan a log file later, you can see what happened and exactly which line of code produced the entry. This is especially helpful in larger codebases where the same log helper is called from dozens of places and you need to distinguish one call site from another without adding manual labels.

Assertions

def assert(condition, message = "Assertion failed")
  return if condition
  raise "Assertion failed: #{message}", caller(1)
end

An assertion helper that uses caller(1) for its backtrace behaves like the built-in Ruby methods. The failure points to the test or validation that called assert, not to the internals of the assertion helper itself. This is the level of API polish that makes a library feel native instead of bolted on, and it needs almost no extra code once you understand how Ruby uses the third argument to raise.

Deprecation Warnings

def deprecated_method(*args)
  warn "deprecated_method is deprecated", caller.first
  # ... old implementation
end

A deprecation warning is only useful when the developer can see which line of their own code triggers it. Using caller.first captures the immediate call site and includes it in the warning message. Without that extra detail, a deprecation notice might list the wrong file or give no file at all, turning a helpful nudge into a frustrating puzzle. The extra string from caller.first costs almost nothing but makes the warning immediately actionable.

Method Tracer

def trace_method(method)
  original = instance_method(method)
  
  define_method(method) do |*args|
    puts "Calling #{method} with #{args.inspect}"
    result = original.bind(self).call(*args)
    puts "Returned: #{result.inspect}"
    result
  end
end

Performance Note

caller creates the full stack trace by walking the Ruby internal call stack frame by frame, which is an operation that scales linearly with stack depth. This makes it noticeably slow in deeply nested call chains because Ruby must allocate a new array and populate it with string representations of every frame from the current position back to the top of the program. For this reason, avoid calling caller inside tight loops or performance-critical paths. When you only need the immediate caller, use caller(1..1) to limit the depth and reduce the allocation cost. In production code, reserve caller for error paths, diagnostic logging, and cases where the stack information genuinely influences program behaviour instead of appearing in every method call.

Reading call stacks wisely

# Common pattern: using caller inside a diagnostic helper
def where_am_i
  loc = caller(1).first
  "Called from #{loc}"
end

def some_deep_method
  puts where_am_i
end

some_deep_method
# => Called from /path/to/file.rb:8:in `some_deep_method'

caller is most useful when you need to answer “where did this come from?” without adding a debugger or a heavy logging setup. The strings it returns are compact, but they still carry enough context to trace a bad value or a surprising method call back to its source. Because the method walks the stack, it is not something to call in a tight loop. Keep it for diagnostics, error reporting, and occasional logging where the extra context is worth the cost.

The optional start and length arguments let you trim the noise so the output points at the caller you actually care about. That is especially helpful in helper methods that sit between the app code and the place where the stack was captured. By skipping wrapper frames, the message becomes easier to read and the code that uses it becomes less distracting. In practice, that means you can keep the diagnostic small without losing the useful part of the trace.

If you are using caller in a library, make the returned text part of a deliberate error or log message instead of a hidden side effect. That keeps the output discoverable and makes it easier to adjust later if the trace format changes. Small, focused stack snippets are usually easier to work with than a wall of frames.

It also helps to think about who will read the trace. A developer usually wants a short path back to the bad call, while a user-facing message may only need a hint that something came from another layer. Keeping those audiences in mind makes it easier to decide how much of the stack to show and where to put it.

See Also