Skip to content
Latchkey

Ruby "can't modify frozen String" in CI - frozen_string_literal

A string literal is frozen - by a # frozen_string_literal: true magic comment or a newer Ruby default - and code tried to mutate it in place. The literal is immutable, so the in-place change raises FrozenError.

What this error means

Code that mutates a string (<<, gsub!, upcase!, []=) raises "FrozenError: can’t modify frozen String", often only on a file with the frozen_string_literal comment or under a newer Ruby. The same code ran on older Ruby without the comment.

Ruby traceback
app/lib/builder.rb:12:in `build': can't modify frozen String:
"prefix-" (FrozenError)
	from app/lib/builder.rb:12:in `<main>'

Common causes

frozen_string_literal makes literals immutable

The magic comment freezes every string literal in the file. In-place mutation of a literal (or a constant assigned a literal) then raises, where a non-frozen literal would have been modified.

Mutating a shared/constant string

A string assigned to a constant or returned from a frozen source is shared and frozen; mutating it in place is rejected.

How to fix it

Duplicate before mutating, or build a new string

Work on a mutable copy, or use non-mutating operations that return a new string.

Ruby
s = (+"prefix-")        # unary + gives a mutable copy
s << name
# or avoid in-place mutation
result = "prefix-" + name

Be deliberate about the magic comment

  1. Keep frozen_string_literal: true and fix the mutations - it is faster and safer.
  2. Use String#dup or unary + where a mutable buffer is genuinely needed.
  3. Run tests under the frozen-string default to catch mutations early.

How to prevent it

  • Adopt frozen_string_literal: true and avoid mutating literals.
  • Use +"" or dup for mutable string buffers.
  • Test under the newer Ruby string defaults to catch FrozenError early.

Related guides

Tired of flaky CI? Latchkey auto-heals failed jobs and retries them for you. Start free →