http://www.perlmonks.org?node_id=570712

You tried, directly or indirectly, to change the value of a constant. This type of error frequently occurs at a distance and is difficult to trace. The magic of $_ is often involved, but is not responsible.

Common causes:

  1. Treating a loop variable as an lvalue
  2. Modifying $_ inside foreach, map or grep
  3. Modifying elements of @_ directly
  4. Modifying $a or $b inside sort
  5. Autovivifying $a or $b inside sort
  6. Modifying an unlocalized $_

Treating a loop variable as an lvalue

for my $x (1,2) { $x++; }
In this example $x is aliased to the constant '1', so when the loop body attempts to increment $x an error is triggered. See Lists With Constant Values for more details.

Modifying $_ inside foreach, map or grep

for (1,2) { chomp; } for ("foo", @list) { s/foo/bar/; } @array = map { $_++ } (1,2); @array = grep { $_++ } (1,2);
In all of these examples $_ is aliased to a constant, and when the loop body attempts to modify $_ an error is triggered. See Lists With Constant Values for more details.

Modifying elements of @_ directly

sub incr { $_[0]++; } my $n = 1; incr($n); # good incr(1); # bad
Modifying elements of @_ directly allows you to modify the variable that was passed to the function. For example, in the above example $n is now 2. But an error will occur when a constant is passed, as in the second call.

Modifying $a or $b inside sort

@array = sort { $a++ } (1,2);
It is permissible (but ill-advised) to modify $a and $b within sort. However, modifying a constant that is aliased to $a or $b is still an error.

Autovivifying $a or $b inside sort

my @bad; $bad[0] = [1]; $bad[2] = [2]; @bad = sort {$a->[0] <=> $b->[0]} @bad;
The variables $a and $b are aliased to each item in the list being sorted, and as such modifying them is possible - but will cause an error if the current element is unmodifiable. A common cause of this is sorting an array of references when where the list has a gap. In this situation $a will be undef, and autovivification by dereferencing will trigger an error.

Modifying an unlocalized $_

for (1,2) { my $data = prompt_user(); } sub prompt_user { print "Enter a number\n"; while (<STDIN>) { # Do stuff } }
This example will cause an error because the for loop aliases $_ to the literal '1', and then calls prompt_user which attempts to read a line from STDIN and store it in $_ - which is still aliased to '1'.

The error will also occur in this simplified scenario:

for (1,2) { while (<STDIN>) { } }

Guidelines to avoid read-only errors:

  1. Don't treat loop variables as a lvalue if there is any chance a constant value will be included
  2. Don't modify an unlocalized $_
  3. Don't modify $_ inside map or grep
  4. Don't modify $a or $b inside sort
  5. Don't dereference $a or $b inside sort without checking that they exist and are references
  6. Don't use $_ for the loop variable unless it is a very trivial loop
  7. Don't modify elements of @_ directly

Notes

Lists With Constant Values

Within the context of this document the important thing to understand is what expressions result in modifiable objects. The following expressions have constants in them:
$_++ for (1,2); $_++ for (1,@array); @array = map {$_++} (1,@array);
And the following are safe:
my @array = (1,2); for (@array) { $_++; }
my ($x,$y) = (1,2); for ($x,$y) { $_++; }
For an explanation of lists versus arrays I recommend the following:

Replies are listed 'Best First'.
Re: Common Causes for "Modification of a read-only value attempted"
by techcode (Hermit) on Jul 15, 2009 at 10:59 UTC
    I've been trying to figure what is going on, as I was getting this error - but wasn't modifying anything that's static.

    Turns out - in an array assignment I missed the comma , between two undef's. Such as:

    my @array = ('something', 'whatever', undef, undef undef, 'foo', 'bar', undef);

    Have you tried freelancing/outsourcing? Check out Scriptlance - I work there since 2003. For more info about Scriptlance and freelancing in general check out my home node.
Re: Common Causes for "Modification of a read-only value attempted"
by maxx27 (Novice) on Jul 10, 2016 at 15:12 UTC
    I have one more case:
    $123 = 0.5;