A sudoers syntax error printed above the denial?
That rule was dropped. Fix sudo-rs "wildcards are not allowed in command arguments" covers the rules sudo-rs rejects and has a script that finds them before the upgrade.
After an upgrade to Ubuntu 26.04, a deploy script that ran sudo -n for years stops with I'm sorry deploy. I'm afraid I can't do that. The message is the same whether the user has no rule, the rule was thrown away, or one argument differs by a slash, and it does not say which. Retyping the command with sudo in front of it changes nothing.
Why Does sudo Say "I'm Afraid I Can't Do That" Now?
Ubuntu 26.04 makes sudo-rs the default /usr/bin/sudo. It reads the same /etc/sudoers, but its messages are different, and it refuses a command with no matching rule before asking for a password. Reproduced on 2026-10-10 in an ubuntu:26.04 container on this machine (Ubuntu 26.04.1 LTS, sudo-rs 0.2.13-0ubuntu1.2, and the classic sudo 1.9.17p2 that the sudo package installs beside it as sudo.ws). Three users: deploy with two NOPASSWD rules for du, intern with no rules, and ops in the sudo group with a password.
| What happened | sudo-rs 0.2.13 | classic sudo 1.9.17p2 |
|---|---|---|
deploy runs sudo -n du -sh /var/log/ (rule says /var/log) | I'm sorry deploy. I'm afraid I can't do that | a password is required |
intern (no rules) runs sudo -n id | I'm sorry intern. I'm afraid I can't do that | a password is required |
ops (needs a password) runs sudo -n id | interactive authentication is required | a password is required |
ops types a wrong password | Authentication failed, try again. | Sorry, try again. |
| password prompt | [sudo: authenticate] Password: | [sudo] password for ops: |
a command only in ~/bin | 'mytool': command not found | mytool: command not found |
Classic sudo said "a password is required" for three different situations; sudo-rs separates them. That is more useful once you know it, and it breaks anything that matches on the old text: monitoring that greps logs for a password is required, and automation that waits for password for. Ansible's become was one of them on 26.04 (the fix and the ansible_become_exe=/usr/bin/sudo.ws workaround are on this Ask Ubuntu question).
What Are the Four Causes?
1. No rule for this user. intern has no rule and is not in the sudo group. sudo-rs says so plainly once you ask it to list:
2. A rule sudo-rs dropped. Every sudo call in this container printed this above its real answer, for every user:
The first three lines are not part of the denial. They say line 2 of deploy's file was skipped, so du -sh /var/log/app-web is no longer allowed. Note that intern and ops saw the same lines, including the rule's text: a sudoers file with a parse error shows its contents to every user who runs sudo.
3. Arguments that do not match exactly. The rule is /usr/bin/du -sh /var/log; the call was du -sh /var/log/. One trailing slash, no match. sudo -l with a command line tests that exact line:
Exit 0 and the resolved path means allowed. Exit 1 and nothing printed means no rule matches.
4. A command sudo cannot find. sudo replaces your PATH with secure_path from /etc/sudoers. On 26.04 that is /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/snap/bin, so a tool in ~/bin or a venv is invisible:
(The password here is the container's demo password.) Use the full path, and name that path in the rule.
Not a denial: interactive authentication is required. The rule matched, but it needs a password and -n, cron or a systemd unit cannot type one. Add NOPASSWD: to that one command's rule, not to ALL.
The Script
Save as why-sudo-denied.sh and run it as the user who was denied, with the same command and arguments that failed. It never prompts (everything goes through sudo -n), so it is safe to call from another script.
Prerequisites
bash 4 or later, coreutils and sudo (either sudo-rs or classic sudo; the checks use sudo -n -l, which both support). No root: it has to run as the denied user to see what that user sees.
How Does Each Check Work?
readlink -fon sudo shows which implementation answered. On 26.04,/usr/bin/sudoresolves to/usr/lib/cargo/bin/sudo(sudo-rs) unless someone switched it tosudo.ws.sudo -n -lstderr carries any/etc/sudoersparse errors, which sudo-rs prints on every call. The script lifts out thefile:line:collines so a dropped rule is named first, before anything else is blamed.sudo -n -lstdout is the list of rules that loaded for this user. If it fails with an authentication message, the user has rules that need a password, and the script stops with exit 3 rather than guessing.sudo -n -l COMMAND ARGSis sudo's own matcher answering for the exact command line. Nothing in the script reimplements sudoers matching.command -vagainst secure_path separates "the rule has different arguments" from "sudo resolves this name to a path no rule mentions" from "sudo cannot find it at all".
What Does the Script Print?
The same container and users as above:
For the user with no rules, and for the one whose rules need a password:
Exit 0 is allowed, 1 is denied with a reason, 3 is "needs a password, could not check". A deploy pipeline can call it before the real sudo step and fail with the reason in the log instead of a quote from a film.
Should I Switch Back to Classic sudo?
Only to buy time. sudo update-alternatives --set sudo /usr/bin/sudo.ws puts the classic binary back behind /usr/bin/sudo (it is installed by the sudo package), and sudo update-alternatives --auto sudo returns to sudo-rs. Every rule sudo-rs rejected works again, and so does every script that matched the old messages. The fixes on this page and in the wildcard page are what keep the box on the configuration Ubuntu will keep testing. The other changes that arrive with the same upgrade are in Ubuntu 26.04: What Breaks in Bash Scripts.
Frequently Asked Questions
What does I'm afraid I can't do that mean in sudo?
It is sudo-rs's message for a command no sudoers rule allows for your user, as written. Ubuntu 26.04 ships sudo-rs as the default sudo, so the message appears after an upgrade from 24.04 where classic sudo would have said a password is required or user is not allowed to execute. Check what you are allowed with sudo -n -l, and test the exact command with sudo -n -l COMMAND ARGS.
Why does sudo say interactive authentication is required?
A rule matches, but it needs your password, and sudo was told not to ask (-n) or has no terminal to ask on, which is the normal case in cron, CI and systemd units. It is not a denial. Add NOPASSWD to the rule for that one command, or run the step interactively. Classic sudo printed a password is required for the same situation, so scripts that grep for that text need updating.
Why does sudo not find a command that is in my PATH?
sudo replaces your PATH with secure_path from /etc/sudoers, which on Ubuntu 26.04 is /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/snap/bin. A tool in ~/bin, a Python venv or /opt is not on it, and sudo-rs reports 'mytool': command not found. Call the tool by its full path, and make sure the sudoers rule names that same path.
Why did Ansible become stop working on Ubuntu 26.04?
sudo-rs prints a different password prompt, [sudo: authenticate] Password:, instead of classic sudo's [sudo] password for user:, and become plugins that wait for the old prompt never see it. Ansible fixed this upstream; until your version has the fix, point that host at the classic binary with ansible_become_exe=/usr/bin/sudo.ws in the inventory.
How do I switch Ubuntu 26.04 back to the old sudo?
The sudo package installs the classic sudo as /usr/bin/sudo.ws, and update-alternatives decides what /usr/bin/sudo points at: sudo update-alternatives --set sudo /usr/bin/sudo.ws switches back, and sudo update-alternatives --auto sudo returns to sudo-rs. Treat it as a stopgap while you fix the rules sudo-rs rejects.
Part of the bash snippets collection
Related Scripts
- Fix sudo-rs "wildcards are not allowed in command arguments" — the rules sudo-rs drops, and a check to run before upgrading
- Ubuntu 26.04: What Breaks in Bash Scripts — the whole list for the 24.04 to 26.04 upgrade
- Fix "bash: command not found" — the PATH side of "command not found", including sudo's secure_path
- Fix Bash "Permission denied" (Exit 126) — when the refusal comes from the filesystem instead