New error in dashboard: R/O variable modified

I’ve never seen this before. Haven’t touched my config in quite a while. I assume an update accomplished this :wink:


Virtualmin Virtual Servers
Version 7.2, master admin mode

Local Mailboxes

HTTP/1.0 500 Perl execution failed Server: MiniServ/2.001 Date: Mon, 10 Oct 2022 19:36:17 GMT Content-type: text/html; Charset=utf-8 Connection: close


Modification of a read-only value attempted at /usr/share/webmin/virtual-server/ line 923.

Operating system Debian Linux 10
Webmin version 2.001
Usermin version 1.860
Virtualmin version 7.2-1
Authentic theme version 20.01.1:5


Have you tried forcefully restarting Webmin by running:


Is this really still necessary? Can Webmin really no longer reliably restart itself (or be restarted on package updates)? If so, that remains our most urgent problem, I think…

No, this is not necessary. Webmin restarts work!

This issue is ambiguous. Force restarting Webmin could tell more about the problem.

My apologies. Life got crazy… catching up. (Had back surgery very soon after this post)

@Ilia further info… perhaps enough to start diagnosing:

  1. this actually happens any time I click on “List Virtual Servers” in VirtualMin
  2. It is 100% repeatable for me right now.
  3. Running the restart-by-force-kill script didn’t appear to do anything interesting. Is there a log I ought to check?


@Jamie, why list_domain_users doesn’t use formal params?

You could check /var/webmin/miniserv.error log file.

We hope you’re doing fine now!

That’s a bug, I’ll check in a fix for it …


Error log is pretty boring :wink:

[09/Dec/2022:16:48:07 -0700] [] /virtual-server/index.cgi : Perl execution failed : Modification of a read-only value attempted at /usr/share/webmin/virtual-server/ line 923.

As for my back… Yes, I’m doing amaaazingly well. In fact, better than I could believe. Bottom line (for the many who have, or know someone with, back issues): a couple of years ago the technology for a large fraction of back surgeries changed. As long as they only need to remove stuff (“decompression”) and not put anything in like screws and spacers (“fusion”), it can now be done using digital microscope and live xray (not laparoscopic/endoscopic)… outpatient instead of staying in the hospital. I went home four hours after arriving, gently exercised the next morning, and was done with pain meds three days later! Eight weeks and I’m no longer restricted from any activities :slight_smile:

My advice to older friends now: don’t wait. Find out if you can get your back issue fixed now; you don’t want to wait for it to get worse! (Fusion surgery is still a big deal with long recovery and risk of complication.)


@Jamie , @Ilia - I did some code reading to attempt understanding of why there’s a read-only value involved at all (since read-only is not exactly a normal Perl concept :wink: )

Not at all sure I’ve identified the real issue, however I did note something that appears potentially dangerous. See also the link below to a list of reasons for such errors:

  1. In list_domains() and many other subs in (and a few other places in the source), a number of temp variables are defined as “local” rather than “my”. This can have huge consequences and I wonder if those who wrote the code understood the implications at the time?***
  • my foo defines a separate fully local-scope variable foo. It is used instead of any global of the same name. It is not visible to any routine called from the current scope (unless passed in.)
  • local foo dynamically redefines global variable foo, and that variable becomes visible to any routines called from within the current context. This remains true until the current context exits.

Obviously they are not interchangeable. Obviously my is what you want for truly local vars.

*** A good reference: scoping - What is the difference between my and local in Perl? - Stack Overflow

  1. I also found a nice list of common reasons for spurious read-only var errors: Common Causes for "Modification of a read-only value attempted"
    Seem most likely the autovivification version, aka spurious use of uninitialized variable.

Not exactly the issue. The source of the problem is that you cannot modify Perl predefined variables like $_[0] or $_ and other, unless assigned to a lexical variable.

You’re right about the use of local … a lot of that code was written a long time ago, and local was the wrong choice. I’m slowly going through and switching over to my. It’s a bit complex though, as there are a few cases where local is actually required, so we can’t just do a global search and replace.

Makes sense… two of the common reasons in the list I provided. Pretty clear why this is subtle!
You don’t necessarily even know that $_ or $_[0] is being modified! A hidden element in function calls.

Someday maybe there will be debug tools that identify such things in more detail. (Might already exist; I have a friend who is an amazing Perl expert… will ask him…)

(and @Jamie) my lives-Perl friend :wink: suggested using the Carp package to reveal what’s up. Nice! It has a replacement for “die”… “confess” shows the calling stack.

use Carp;

confess “xyz”;
a = b or confess; # (normally “or die”)

Then I tried ... or confess; on the error line. That didn’t help. But inserting confess "here"; ahead of that line made it clear:

  • virtual_server::list_domain_users(undef, 0, 1) called at /usr/share/webmin/virtual-server/index.cgi line 71
    • Yep. index.cgi line 71: @lusers = &list_domain_users(undef, 0, 1);
    • So, first param $d or $_[0] is undef
    • So, the line causing this fatal error can’t run:
      my $qfile = $quota_cache_dir."/".$_[0]->{'id'}

(NOTE: the Carp package also has cluck(); which is like confess(); without terminating. A nice call stack for debug purposes. :wink: )
I hope that helps!

And here’s a workaround that is working for me (embed that section in an if ($_[0]) block):

# Merge in cached quotas
if ($_[0]) {
        my $qfile = $quota_cache_dir."/".$_[0]->{'id'};
        my %qcache;
        &read_file_cached($qfile, \%qcache);
        foreach $u (@users) {
                $u->{'quota_cache'} = $qcache{$u->{'user'}."_quota"};
                $u->{'mquota_cache'} = $qcache{$u->{'user'}."_mquota"};

Good catch, I’ll fix that in the next release.

Although, that code path is only executed in the super-rare case where Virtualmin is managing users not associated with a domain.

In my case, I have a hidden domain with associated users, and an aliased domain. The aliased domain is what is publicly visible. Works great in postfix (has worked nicely for over 20 years!)… but yes… VirtualMin thinks there are no associated users in the install :frowning:

I will try to find time to work out why the users are invisible.

This topic was automatically closed 60 days after the last reply. New replies are no longer allowed.