# FrozenError: can't modify frozen String in CI

> Fix "FrozenError: can't modify frozen String" in Ruby 3 CI - code mutates a string literal that is frozen, often via a frozen_string_literal magic comment or Ruby 3 defaults.

Source: https://latchkey.dev/learn/ruby/ruby-frozenerror-cant-modify-frozen-string-in-ci  
Updated: 2026-06-26

Code tried to mutate a frozen String in place (with <<, gsub!, concat, etc.). Ruby 3 and the frozen_string_literal magic comment freeze string literals, so any in-place mutation raises FrozenError.

## Diagnose it: Ruby version and platform

Bundler resolves against the Ruby version and the platform recorded in the lockfile. A runner on a different Ruby or a Linux platform missing from `Gemfile.lock` fails in a way that names a gem rather than the cause.

```Terminal
ruby -v && bundle -v
cat .ruby-version 2>/dev/null
bundle platform

# the usual CI-only failure: Linux platform absent from the lockfile
bundle lock --add-platform x86_64-linux
bundle install --jobs 4 --retry 3
```

> A gem that installs on macOS and fails on a Linux runner is usually a missing platform in `Gemfile.lock`, not a broken gem. Add the platform and commit the lockfile.

## FAQ

### What causes FrozenError: can't modify frozen string in CI?

There are 3 common causes: frozen_string_literal magic comment, ruby version default change, and mutating a shared constant string. A # frozen_string_literal: true comment at the top of the file freezes every string literal in it, so in-place mutation of a literal raises.

### How do I fix FrozenError: can't modify frozen string in CI?

There are 2 fixes depending on which cause you have: build a new string instead of mutating and dup a frozen string when mutation is needed. Work through them in order, since the first is the most common.

### What does FrozenError: can't modify frozen string in CI actually mean?

A test fails with FrozenError on a mutating string method.

### How do I stop FrozenError: can't modify frozen string in CI happening again?

Prefer non-mutating string operations. The prevention section lists 3 changes that keep it from recurring.

---

Latchkey runs CI/CD that repairs its own failures. Agent entry points: https://latchkey.dev/agent.txt, https://latchkey.dev/openapi.json, https://latchkey.dev/llms.txt
