Skip to content

fix: resolve "terminal is required" error when uninstalling Homebrew casks that run embedded sudo installers#1004

Closed
manuelguido wants to merge 1 commit into
tw93:mainfrom
manuelguido:fix/brew-cask-sudo-askpass-no-tty
Closed

fix: resolve "terminal is required" error when uninstalling Homebrew casks that run embedded sudo installers#1004
manuelguido wants to merge 1 commit into
tw93:mainfrom
manuelguido:fix/brew-cask-sudo-askpass-no-tty

Conversation

@manuelguido
Copy link
Copy Markdown
Contributor

@manuelguido manuelguido commented May 29, 2026

Problem

Uninstalling certain Homebrew casks (e.g. Wireshark) fails with:
sudo: a terminal is required to read the password
sudo: a password is required
Error: Failure while executing; /usr/bin/sudo … /usr/sbin/installer … exited with 1.

Homebrew cask uninstall scripts can call sudo directly for embedded
.pkg installers. Mole runs brew uninstall through a timeout wrapper
whose Perl fallback (run_with_timeout) detaches the controlling
terminal, so the inner sudo has no TTY and cannot prompt for a password.

Fix

lib/core/sudo.shcreate_sudo_askpass_helper()

Adds a new helper that writes a minimal temp script (mktemp, mode 700)
which shows a native macOS GUI password dialog via osascript. The
password is never written to disk; sudo only invokes the helper when it
actually needs one (a still-valid timestamp skips the prompt entirely).
The function is a no-op in test mode and on non-macOS systems.

lib/uninstall/brew.shbrew_uninstall_cask()

Before calling brew uninstall, exports SUDO_ASKPASS to the helper
path. Homebrew automatically appends sudo -A when SUDO_ASKPASS is
set, so any nested sudo inside a cask script can obtain the password
through the GUI without needing a TTY.

In the sudo -u "$SUDO_USER" env … branch the variable is re-injected
explicitly via the inner env list (the outer sudo strips the
environment). Cleanup (temp file removal + SUDO_ASKPASS restore) is
done inline after brew exits — not via a RETURN trap, which in bash
3.2 is not function-scoped and would re-fire on the caller's return with
locals out of scope, aborting under set -u.

tests/brew_uninstall.bats

Two new tests:

  • brew_uninstall_cask exports SUDO_ASKPASS so inner sudo prompts work — verifies the variable is visible to brew during uninstall.
  • brew_uninstall_cask restores a pre-existing SUDO_ASKPASS value — verifies the prior value is restored after the call.

Resolves #1003

@manuelguido manuelguido requested a review from tw93 as a code owner May 29, 2026 14:28
@tw93
Copy link
Copy Markdown
Owner

tw93 commented May 29, 2026

@manuelguido Thank you for the clear write-up. Your root-cause analysis (the Perl fallback detaching the TTY) is exactly right.

I fixed #1003 on main from the other direction: the timeout fallback now keeps the controlling terminal (setpgid(0,0) instead of setsid()), so the nested sudo can read the password on the existing terminal and reuse the already-cached credential, without adding an osascript password dialog (aa9a371). Since that resolves the same root cause, I'm closing this as superseded.

I really appreciate the contribution and the tests; your TTY analysis directly informed the fix. Thank you!

@tw93 tw93 closed this May 29, 2026
@manuelguido manuelguido deleted the fix/brew-cask-sudo-askpass-no-tty branch May 29, 2026 17:24
@manuelguido
Copy link
Copy Markdown
Contributor Author

@tw93 Perfect! Way cleaner than my approach. Keeping the TTY directly makes much more sense. Thank you for this great project!

I stopped using other tools to clean my mac since I found this one 🔥

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG] Permission issues when removing some Homebrew apps

2 participants