Plinth can't read and set access rights for Radicale

Problem Description
I’m using Freedomox from 2017. I’ve migrated Radicale to version 2 in 2019.
Some time ago I noticed that Plinth can’t display and change access rights for calendars,
that means no radio button is on.
Also setings any access right fails with error 500.

Steps to Reproduce

  1. Login to FreedomBox as admin user
  2. Go to Radicale application page.
  3. Choose for instance radio button Only the owner of a calendar/addressbook can view or make changes
  4. Push button Update setup

I’ve also extracted from logs command which sets up access right, but it fails too.

root@fbx:/home/fbx# /usr/share/plinth/actions/radicale configure --rights_type owner_only

Traceback (most recent call last):
  File "/usr/share/plinth/actions/radicale", line 76, in <module>
  File "/usr/share/plinth/actions/radicale", line 72, in main
  File "/usr/share/plinth/actions/radicale", line 41, in subcommand_configure
  File "/usr/lib/python3/dist-packages/", line 488, in save
    raise IOError("Unable to save to file!")
OSError: Unable to save to file!

Expected Results
Access rights set as radio button selected.

Actual results
This is an internal error and not something you caused or can fix. Please report the error on the bug tracker so we can fix it. Also, please attach the status log to the bug report.

status log

Dec 21 18:18:48 fbx /usr/bin/plinth[458]: Error executing command - ['sudo', '-n', '/usr/share/plinth/actions/radicale', 'configure', '--rights_type', 'owner_only'], , Traceback (most recent call last):
                                            File "/usr/share/plinth/actions/radicale", line 76, in <module>
                                            File "/usr/share/plinth/actions/radicale", line 72, in main
                                            File "/usr/share/plinth/actions/radicale", line 41, in subcommand_configure
                                            File "/usr/lib/python3/dist-packages/", line 488, in save
                                              raise IOError("Unable to save to file!")
                                          OSError: Unable to save to file!
Dec 21 18:18:48 fbx /usr/bin/plinth[458]: Internal Server Error: /plinth/apps/radicale/
                                          Traceback (most recent call last):
                                            File "/usr/lib/python3/dist-packages/django/core/handlers/", line 41, in inner
                                              response = get_response(request)
                                            File "/usr/lib/python3/dist-packages/django/core/handlers/", line 187, in _get_response
                                              response = self.process_exception_by_middleware(e, request)
                                            File "/usr/lib/python3/dist-packages/django/core/handlers/", line 185, in _get_response
                                              response = wrapped_callback(request, *callback_args, **callback_kwargs)
                                            File "/usr/lib/python3/dist-packages/django/views/generic/", line 68, in view
                                              return self.dispatch(request, *args, **kwargs)
                                            File "/usr/lib/python3/dist-packages/django/views/generic/", line 88, in dispatch
                                              return handler(request, *args, **kwargs)
                                            File "/usr/lib/python3/dist-packages/plinth/", line 163, in post
                                              return super().post(request, *args, **kwargs)
                                            File "/usr/lib/python3/dist-packages/django/views/generic/", line 183, in post
                                              return self.form_valid(form)
                                            File "/usr/lib/python3/dist-packages/plinth/modules/radicale/", line 33, in form_valid
                                              ['configure', '--rights_type', data['access_rights']])
                                            File "/usr/lib/python3/dist-packages/plinth/", line 105, in superuser_run
                                            File "/usr/lib/python3/dist-packages/plinth/", line 200, in _run
                                              raise ActionError(action, output, error)
                                          plinth.errors.ActionError: ('radicale', '', 'Traceback (most recent call last):\n  File "/usr/share/plinth/actions/radicale", line 76, in <module>\n    main()\n  File "/usr/share/plinth/actions/radicale", line 72, in main\n    subcommand_method(arguments)\n  File "/usr/share/plinth/actions/radicale", line 41, in subcommand_configure\n\n  File "/usr/lib/python3/dist-packages/", line 488, in save\n    raise IOError("Unable to save to file!")\nOSError: Unable to save to file!\n')
Dec 21 18:19:04 fbx /usr/bin/plinth[458]: # storage usage-info



  • FreedomBox version: 21.4.4
  • Hardware: Raspberry Pi 3 Model B
  • How did you install FreedomBox?: downloading testing images from freedombox site

Could you please post the contents of /etc/radicale/config?

Thank you for your answer.
My /etc/radicale/config

# -*- mode: conf -*-
# vim:ft=cfg

# Config file for Radicale - A simple calendar server
# Place it into /etc/radicale/config (global)
# or ~/.config/radicale/config (user)
# The current values are the default ones


# CalDAV server hostnames separated by a comma
# IPv4 syntax: address:port
# IPv6 syntax: [address]:port
# For example:, [::]:9999
#hosts =

# Daemon flag
#daemon = False

# File storing the PID in daemon mode
#pid =

# Max parallel connections
#max_connections = 20

# Max size of request body (bytes)
#max_content_length = 100000000

# Socket timeout (seconds)
#timeout = 30

# SSL flag, enable HTTPS protocol
#ssl = False

# SSL certificate path
certificate = /etc/ssl/certs/ssl-cert-snakeoil.pem

# SSL private key
key = /etc/ssl/private/ssl-cert-snakeoil.key

# CA certificate for validating clients. This can be used to secure
# TCP traffic between Radicale and a reverse proxy
#certificate_authority =

# SSL Protocol used. See python's ssl module for available values
#protocol = PROTOCOL_TLSv1_2

# Available ciphers. See python's ssl module for available ciphers
#ciphers =

# Reverse DNS to resolve client address in logs
#dns_lookup = True

# Message displayed in the client when a password is needed
#realm = Radicale - Password Required


# Encoding for responding requests
#request = utf-8

# Encoding for storing local collections
#stock = utf-8


# Authentication method
# Value: none | htpasswd | remote_user | http_x_remote_user
type = remote_user

# Htpasswd filename
#htpasswd_filename = /etc/radicale/users

# Htpasswd encryption method
# Value: plain | sha1 | ssha | crypt | bcrypt | md5
# Only bcrypt can be considered secure.
# bcrypt and md5 require the passlib library to be installed.
#htpasswd_encryption = bcrypt

# Incorrect authentication delay (seconds)
#delay = 1


# Rights backend
# Value: none | authenticated | owner_only | owner_write | from_file
type = owner_write

# File for rights management from_file
file = /etc/radicale/rights


# Storage backend
# Value: multifilesystem
#type = multifilesystem

# Folder for storing local collections, created if not present
#filesystem_folder = /var/lib/radicale/collections

# Lock the storage. Never start multiple instances of Radicale or edit the
# storage externally while Radicale is running if disabled.
#filesystem_locking = True

# Sync all changes to disk during requests. (This can impair performance.)
# Disabling it increases the risk of data loss, when the system crashes or
# power fails!
#filesystem_fsync = True

# Delete sync token that are older (seconds)
#max_sync_token_age = 2592000

# Close the lock file when no more clients are waiting.
# This option is not very useful in general, but on Windows files that are
# opened cannot be deleted.
#filesystem_close_lock_file = False

# Command that is run after changes to storage
# Example: ([ -d .git ] || git init) && ([ -e .gitignore ] || printf '.Radicale.cache\n.Radicale.lock\n.Radicale.tmp-*\n' > .gitignore) && git add -A && (git diff --cached --quiet || git commit -m "Changes by "%(user)s)
hook = ([ -d .git ] || git init) && ([ -e .gitignore ] || printf '.Radicale.cache\n.Radicale.lock\n.Radicale.tmp-*\n' > .gitignore) && git add -A && (git diff --cached --quiet || git commit -m "Changes by Radicale server; user: "%(user)s)


# Web interface backend
# Value: none | internal
#type = internal


# Logging configuration file
# If no config is given, simple information is printed on the standard output
# For more information about the syntax of the configuration file, see:
#config =

# Set the default logging level to debug
#debug = False

# Store all environment variables (including those set in the shell)
#full_environment = False

# Don't include passwords in logs
#mask_passwords = True


# Additional HTTP headers
#Access-Control-Allow-Origin = *

I’m curious about the “hook” line under [storage]. Did you set this on purpose?

In my file it is just an empty comment: #hook =
May be worth seeing if commenting it has any effect.

The hook commits changes in radicale collections to the local git repository every time they are changed. Details are here Versioning with Git.
You are right. When I disabled the hook and rebooted my Raspberry Pi radio buttons has started to work. That means every time only one radio button is on and I’m able to switch Access rights as I wish.
I did a quick git test and it occurred that git repository is broken.

root@fbx:/var/lib/radicale/collections# git status
fatal: detected dubious ownership in repository at '/var/lib/radicale/collections'
To add an exception for this directory, call:

	git config --global --add /var/lib/radicale/collections

I fixed the “dubious ownership” problem, but it was not enough.
Still all radio buttons were disabled.
Only removing the semicolon from hook solved the problem.

Correct “hook”

hook = git add -A && (git diff --cached --quiet || git commit -m "Changes by "%(user)s)