skip to content

A project's .ruby-version pins 4.0.7 under rbenv, yet a newly opened terminal runs the system Ruby there; how do you diagnose and fix it?

level: seniorimportance: should knowfreq 35%

answer

  1. is the shims dir first
  2. type -a ruby shows the order
  3. login vs non-login startup files
  4. stray RBENV_VERSION outranks the file
  5. rbenv version names the origin

basics

~20 s

Check that ~/.rbenv/shims comes first on PATH with type -a ruby; usually the rbenv init line sits in a startup file that shell never reads, or a later PATH edit wins. Then run rbenv version to spot a stray RBENV_VERSION.

solid answer

~40 s

I split it into two checks. First, is rbenv reached at all: `type -a ruby` must list `~/.rbenv/shims/ruby` first. If the shims directory is missing, the shell never evaluated `rbenv init -`; with rbenv 1.3.2, `rbenv init` typically writes that line to `~/.zprofile` for zsh or `~/.bash_profile` for bash, which non-login shells skip, so I move it to the file this terminal reads. If shims are present but not first, a later PATH edit put another Ruby ahead. Second, what does rbenv resolve: `rbenv version` names the source, and an inherited `RBENV_VERSION` beats `.ruby-version`, so I unset it. In CI the same bug appears because non-interactive shells read no profile; there I use `rbenv exec` or `eval "$(rbenv init - bash)"`.

code

bash · 10 lines
bash
cd ~/code/billing-worker
type -a ruby          # /usr/bin/ruby first -> shims not first on PATH
echo "$PATH" | tr ':' '\n' | grep -n rbenv
grep -n 'rbenv init' ~/.zprofile ~/.zshrc ~/.bashrc ~/.bash_profile 2>/dev/null
rbenv version         # 3.4.7 (set by RBENV_VERSION environment variable)?
env | grep RBENV_VERSION
# fix: load rbenv in the file this shell reads, drop the stray export
echo 'eval "$(rbenv init - --no-rehash zsh)"' >> ~/.zshrc
exec zsh
type -a ruby          # ~/.rbenv/shims/ruby first

go deeper

for a junior

Recall that rbenv only works when its shims directory is first on PATH, and that rbenv version tells you which setting chose the Ruby.

for a middle

Explain which startup file rbenv init writes to, why login and non-login shells differ, and how RBENV_VERSION outranks .ruby-version.

for a senior

Run the two-step diagnosis quickly: PATH order with type -a, then the origin from rbenv version, and fix CI with rbenv exec or an explicit init eval.

for a principal

Standardise how developer machines and CI load the version manager so environment drift shows up as a clear failure, not a silently different Ruby.

## Two separate questions When a project pinned with `.ruby-version` runs the wrong Ruby in a fresh terminal, split the problem in two: 1. **Is rbenv even reached?** If `~/.rbenv/shims` is not first on `PATH`, the shell runs some other `ruby` and rbenv never sees the call. No rbenv setting can fix this. 2. **If it is reached, what does it resolve?** rbenv may be working perfectly and picking a different source than the file you expect. It is often the first kind, and the "new terminal" detail is the clue: an old terminal works, a new one does not, so something in shell startup differs. ## Step 1: is the shim reached? 1. `type -a ruby` (or `which -a ruby`) lists every `ruby` on `PATH` in order. The first line must be `~/.rbenv/shims/ruby`. 2. `echo "$PATH" | tr ':' '\n'` shows whether the shims directory is present at all, and what comes before it. 3. `command -v rbenv` confirms the `rbenv` command itself is reachable. If the shims directory is **missing**, the shell never evaluated `rbenv init -`. If it is **present but not first**, something prepended another Ruby after rbenv's line ran. ## Why a new terminal skips rbenv Since rbenv 1.3.0, running `rbenv init` with no arguments writes an `eval "$(rbenv init - --no-rehash <shell>)"` line into a startup file for you. Which file it picks decides which shells load rbenv: | Shell | File `rbenv init` 1.3.2 writes to | |---|---| | zsh | `~/.zprofile`, unless `~/.zshrc` already mentions rbenv | | bash | `~/.bashrc` if it exists and `~/.bash_profile` does not; otherwise the first of `~/.bash_profile`, `~/.bash_login`, `~/.profile` | | fish | `~/.config/fish/config.fish` | | ksh | `~/.profile` | Common failure patterns: - **Login vs non-login shells.** `~/.zprofile` and `~/.bash_profile` are read by login shells only. A terminal emulator, editor or multiplexer that starts a non-login interactive shell reads `~/.zshrc` or `~/.bashrc` instead, and never evaluates the rbenv line. - **A later PATH edit.** A line after the eval, or a file read after it, prepends another Ruby's `bin` directory (a package manager's Ruby, a language toolchain installer), so that Ruby wins. - **Testing in the old terminal.** Editing a startup file and re-checking in the same window proves nothing; only a fresh window runs startup the way the next terminal will. - **A shell that cached a path.** The shell's command hash can remember `/usr/bin/ruby` from before; `hash -r` clears it, and rbenv's integrated `rbenv rehash` runs it too. ## Step 2: what does rbenv resolve? Run `rbenv version`. The origin text names the culprit: - `(set by RBENV_VERSION environment variable)`: something exported the variable, and it outranks `.ruby-version`. Typical sources are an `export RBENV_VERSION=...` line in a dotfile, an `rbenv shell` run in a parent shell whose environment the new terminal inherited, or a terminal started by a process that ran under `rbenv exec`, which exports `RBENV_VERSION`. - `(set by ~/.rbenv/version)`: rbenv did not find the project's file. Check that it is non-empty, that you are inside the project tree, and that its first word is the version name. - A "version 4.0.7 is not installed" error naming the `.ruby-version` path: the file is read but the version is missing; run `rbenv install` in the project (it installs what `.ruby-version` names). ## Fixes - Move or copy the eval line into the file your terminal's shell actually reads, then open a new terminal to test. - Make sure the rbenv line comes **after** any other PATH edits that add a Ruby, or remove those edits. - Remove stray `RBENV_VERSION` exports; use `rbenv shell --unset` in a live shell. - Re-check with `type -a ruby` and `rbenv version` rather than trusting `ruby -v` alone. ## The same failure in CI and scripts CI steps and cron jobs usually run a **non-interactive** shell that reads neither profile file, so shims are never on `PATH` and the image's system Ruby runs. Reliable options: - Evaluate `eval "$(rbenv init - bash)"` at the start of the step. - Prepend `"$(rbenv root)/shims"` to `PATH` explicitly. - Call commands through `rbenv exec`, which resolves the version without any shell integration. - Install with `rbenv install -s` (no version argument) so a cached runner builds the pinned Ruby once and skips it afterwards.

  • In a CI job, the tests run under the image's system Ruby although rbenv and the pinned version are installed; why, and what fixes it?
    The job's steps run in a non-interactive shell that reads no profile file, so `rbenv init -` never runs and the shims are not on `PATH`. Fix it inside the job: evaluate `eval "$(rbenv init - bash)"` first, prepend `$(rbenv root)/shims` to `PATH`, or run commands through `rbenv exec`. Pair it with `rbenv install -s` so a cached runner installs the pinned Ruby only once.
  • rbenv version in the new terminal prints 3.4.7 (set by RBENV_VERSION environment variable); where can that come from?
    Something put `RBENV_VERSION` into the environment before the shell started: an `export` in a dotfile, an `rbenv shell` in a parent shell that launched this terminal or multiplexer, or a parent process started through `rbenv exec`, which exports the resolved `RBENV_VERSION`. Remove the source; `rbenv shell --unset` clears it in the live shell.

saying these in an interview costs you the question

  • rbenv rehash puts a missing shims directory back on PATH
  • Placing the rbenv init line anywhere in a profile guarantees rbenv wins PATH
  • rbenv only reads .ruby-version when you cd into the directory
  • Exporting RBENV_VERSION in the profile is a good way to pin one project
  • Reinstalling Ruby 4.0.7 fixes a .ruby-version the terminal ignores