Skip to content

Fix sudo-rs "I'm sorry user. I'm afraid I can't do that" on Ubuntu 26.04

sudoubuntusudoerstroubleshootingsecurity
7 min read

Quick Answer

sudo: I'm sorry deploy. I'm afraid I can't do that is how sudo-rs, the default sudo on Ubuntu 26.04, says no sudoers rule allows that exact command for that user. Classic sudo usually asked for a password first and said a password is required when run with -n; sudo-rs refuses before any prompt. Four causes produce it. The user has no rule at all. The rule exists but sudo-rs dropped it while parsing, which it announces with a sudoers syntax error printed above the denial. The arguments differ from the rule, even by a trailing slash, because arguments must match exactly. Or the command is called by a name that resolves to a different path. Run sudo -n -l as the user to see the rules sudo-rs actually loaded, and sudo -n -l COMMAND ARGS to test one exact command line: it prints the full path and exits 0 only if allowed. why-sudo-denied.sh on this page runs those checks and names the cause.

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 happenedsudo-rs 0.2.13classic 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 thata password is required
intern (no rules) runs sudo -n idI'm sorry intern. I'm afraid I can't do thata password is required
ops (needs a password) runs sudo -n idinteractive authentication is requireda password is required
ops types a wrong passwordAuthentication failed, try again.Sorry, try again.
password prompt[sudo: authenticate] Password:[sudo] password for ops:
a command only in ~/bin'mytool': command not foundmytool: 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:

text
$ sudo -n -l sudo: Sorry, user intern may not run sudo on laptop.

2. A rule sudo-rs dropped. Every sudo call in this container printed this above its real answer, for every user:

text
$ sudo -n du -sh /var/log/ /etc/sudoers.d/deploy:2:29: wildcards are not allowed in command arguments deploy ALL=(root) NOPASSWD: /usr/bin/du -sh /var/log/app-* ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ sudo: I'm sorry deploy. I'm afraid I can't do that exit=1

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:

text
$ sudo -n -l du -sh /var/log /usr/bin/du -sh /var/log exit=0 $ sudo -n -l du -sh /etc exit=1

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:

text
$ echo s3cret-demo | PATH=$HOME/bin:$PATH sudo -S -k mytool [sudo: authenticate] Password: sudo: 'mytool': command not found exit=1

(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.

bash
#!/bin/bash # Script: why-sudo-denied.sh # Purpose: sudo-rs answers "I'm afraid I can't do that" for a missing rule, a rule it dropped as unparseable and a one-character argument mismatch alike; this says which one it is. # Usage: ./why-sudo-denied.sh COMMAND [ARGS...] (run as the user who was denied, without sudo) set -euo pipefail CHECK="✓" CROSS="✗" [[ $# -ge 1 ]] || { echo "usage: $0 COMMAND [ARGS...]" >&2; exit 2; } [[ $EUID -ne 0 ]] || { echo "$CROSS run this as the user who was denied, not as root" >&2; exit 2; } SUDO_BIN=$(command -v sudo) || { echo "$CROSS sudo is not installed"; exit 1; } # Ubuntu 26.04 points /usr/bin/sudo at sudo-rs through update-alternatives; sudo.ws is the classic sudo. echo " sudo: $(readlink -f "$SUDO_BIN") ($("$SUDO_BIN" --version 2>/dev/null | head -1))" echo " you: $(id -un), groups: $(id -nG)" # -n never prompts, so this cannot hang a script; stderr carries any sudoers lines the parser threw away. LIST_ERR=$(mktemp) trap 'rm -f "$LIST_ERR"' EXIT LIST_OUT=$("$SUDO_BIN" -n -l 2>"$LIST_ERR") && LIST_RC=0 || LIST_RC=$? if grep -q 'syntax error\|not allowed\|not supported\|illegal' "$LIST_ERR"; then echo "$CROSS sudoers has lines sudo-rs could not parse; those rules are ignored:" grep -E '^/etc/sudoers' "$LIST_ERR" | sed 's/^/ /' echo " fix them (sudoers-rs-check.sh lists each one); classic sudo, if installed, is sudo.ws" fi if (( LIST_RC != 0 )); then if grep -qi 'authenticat\|password is required' "$LIST_ERR"; then # Not a denial: a rule matches you but wants a password, and -n (or cron, or ansible) cannot type one. echo " you have rules, but they need your password, so -n cannot check them." echo " 'interactive authentication is required' from a script means exactly this: add NOPASSWD" echo " for that one command, or run: sudo -l $* (it prompts, then prints the match or nothing)" exit 3 else echo "$CROSS sudo -l failed: $(tail -1 "$LIST_ERR")" echo " no rule names you or your groups: an admin has to add one (visudo -f /etc/sudoers.d/$(id -un))" exit 1 fi else echo " rules for you:" grep -E '^[[:space:]]+\(' <<< "$LIST_OUT" | sed 's/^[[:space:]]*/ /' fi # sudo -l COMMAND ARGS answers "would this exact command line be allowed?" with exit 0 and the resolved path. if MATCH=$("$SUDO_BIN" -n -l "$@" 2>/dev/null); then echo "$CHECK allowed: $MATCH" if ! grep -q 'NOPASSWD' <<< "$LIST_OUT"; then echo " it needs your password: in a script without a terminal sudo -n stops with 'interactive authentication is required'" fi exit 0 fi echo "$CROSS not allowed: $*" RESOLVED=$(PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" command -v "$1" || true) if [[ -z "$RESOLVED" ]]; then # secure_path replaces your PATH, so a tool in ~/bin or a venv is invisible to sudo. echo " $1 is not on sudo's secure_path; call it by its full path, and the rule must name that path" elif grep -qF "$RESOLVED" <<< "$LIST_OUT"; then echo " a rule names $RESOLVED, but not with these arguments. Arguments must match exactly" echo " (a trailing slash or a different order counts). Rules for it:" grep -F "$RESOLVED" <<< "$LIST_OUT" | sed 's/^[[:space:]]*/ /' else echo " no rule mentions $RESOLVED" fi exit 1

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 -f on sudo shows which implementation answered. On 26.04, /usr/bin/sudo resolves to /usr/lib/cargo/bin/sudo (sudo-rs) unless someone switched it to sudo.ws.
  • sudo -n -l stderr carries any /etc/sudoers parse errors, which sudo-rs prints on every call. The script lifts out the file:line:col lines so a dropped rule is named first, before anything else is blamed.
  • sudo -n -l stdout 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 ARGS is sudo's own matcher answering for the exact command line. Nothing in the script reimplements sudoers matching.
  • command -v against 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:

text
$ why-sudo-denied.sh du -sh /var/log/ sudo: /usr/lib/cargo/bin/sudo (sudo-rs 0.2.13-0ubuntu1.2) you: deploy, groups: deploy ✗ sudoers has lines sudo-rs could not parse; those rules are ignored: /etc/sudoers.d/deploy:2:29: wildcards are not allowed in command arguments fix them (sudoers-rs-check.sh lists each one); classic sudo, if installed, is sudo.ws rules for you: (root) NOPASSWD: /usr/bin/du -sh /var/log ✗ not allowed: du -sh /var/log/ a rule names /usr/bin/du, but not with these arguments. Arguments must match exactly (a trailing slash or a different order counts). Rules for it: (root) NOPASSWD: /usr/bin/du -sh /var/log exit=1 $ why-sudo-denied.sh du -sh /var/log sudo: /usr/lib/cargo/bin/sudo (sudo-rs 0.2.13-0ubuntu1.2) you: deploy, groups: deploy ✗ sudoers has lines sudo-rs could not parse; those rules are ignored: /etc/sudoers.d/deploy:2:29: wildcards are not allowed in command arguments fix them (sudoers-rs-check.sh lists each one); classic sudo, if installed, is sudo.ws rules for you: (root) NOPASSWD: /usr/bin/du -sh /var/log ✓ allowed: /usr/bin/du -sh /var/log exit=0

For the user with no rules, and for the one whose rules need a password:

text
$ why-sudo-denied.sh id sudo: /usr/lib/cargo/bin/sudo (sudo-rs 0.2.13-0ubuntu1.2) you: intern, groups: intern ✗ sudoers has lines sudo-rs could not parse; those rules are ignored: /etc/sudoers.d/deploy:2:29: wildcards are not allowed in command arguments fix them (sudoers-rs-check.sh lists each one); classic sudo, if installed, is sudo.ws ✗ sudo -l failed: sudo: Sorry, user intern may not run sudo on laptop. no rule names you or your groups: an admin has to add one (visudo -f /etc/sudoers.d/intern) exit=1 $ why-sudo-denied.sh id sudo: /usr/lib/cargo/bin/sudo (sudo-rs 0.2.13-0ubuntu1.2) you: ops, groups: ops sudo ✗ sudoers has lines sudo-rs could not parse; those rules are ignored: /etc/sudoers.d/deploy:2:29: wildcards are not allowed in command arguments fix them (sudoers-rs-check.sh lists each one); classic sudo, if installed, is sudo.ws you have rules, but they need your password, so -n cannot check them. 'interactive authentication is required' from a script means exactly this: add NOPASSWD for that one command, or run: sudo -l id (it prompts, then prints the match or nothing) exit=3

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

Raw script, MIT licensed: scripts/sudo-rs-afraid-cant-do-that.sh on GitHub

PAID RESOURCE — $9

The Production Bash Toolkit

An operational script system + a 30-function shared library + a 52-page field guide. The production layer the free snippets don't cover.

Get the Toolkit →
curl -O bashlib-starter.sh

Get the bashlib starter

Ten functions I source into every script on my own boxes — strict-mode setup, an ERR trap that names the failing line, lock and timeout wrappers, and cleanup that runs on every exit path. One email, no sequence.

BashSnippets logo

Written by Travis

Creator of BashSnippets.xyz

bashsnippets.xyz/about

Related Snippets

Frequently Asked Questions

faq — snippet

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.

faq — snippet

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.

faq — snippet

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.

faq — snippet

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.

faq — snippet

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.