Bundler "Gem::FilePermissionError ... write permissions" in CI
By Kaveh Alemi·Latchkey
Bundler or RubyGems tried to write a gem into a directory the current user cannot write to - usually a root-owned system gem path while the job runs as a non-root user. Install into a user-owned or vendored path instead.
What this error means
bundle install or gem install fails with Gem::FilePermissionError "You don’t have write permissions for the /usr/local/... directory". The install targets a system gem dir the job user cannot write.
gem output
Gem::FilePermissionError: You don't have write permissions for the
/usr/local/lib/ruby/gems/3.3.0 directory.
Common causes
Installing into a root-owned system gem dir
The default GEM_HOME is a system path owned by root. A job running as a non-root user cannot write there, so the install is denied.
Mismatched ownership on a cached gem dir
A restored cache or a volume mounted with different ownership leaves the gem directory unwritable by the current user.
How to fix it
Install gems into a vendored path
Direct Bundler to a project-local directory the job owns - no system write needed.
Terminal
bundle config set --local path vendor/bundle
bundle install
Use a user gem dir or fix ownership
Terminal
# install into the user's gem home
export GEM_HOME="$HOME/.gem"
export PATH="$GEM_HOME/bin:$PATH"
# or correct ownership of a cached dir
chown -R "$(id -u):$(id -g)" vendor/bundle
How to prevent it
Set bundle path to vendor/bundle so installs stay project-local.
Run the job and any cache restore as a consistent user.
Avoid sudo gem install in CI; it creates root-owned gems.
Frequently asked questions
What causes ""FilePermissionError""?
The default GEM_HOME is a system path owned by root. A job running as a non-root user cannot write there, so the install is denied.
How do I fix "FilePermissionError"?
Direct Bundler to a project-local directory the job owns - no system write needed.
Can Latchkey fix this automatically?
Yes. Latchkey runs your GitHub Actions on managed runners that detect this failure, apply the fix, and retry the job automatically - self-healing is on by default.