sinelaw/fresh · error · RuntimeError
sudo tee failed
Error message
sudo tee failed: {stderr.decode().strip()} What it means
cmd_sudo_write writes file contents on a remote host by piping data into `sudo tee <path>`. When the sudo/tee process exits non-zero, the helper raises RuntimeError embedding tee's stderr so the editor's remote-write operation fails loudly instead of silently leaving the file unwritten. It usually means sudo could not complete the write: missing sudo credentials, non-interactive sudo, bad path, or filesystem errors.
Solutions
- Read the stderr embedded in the message to see the actual sudo/tee failure cause (password prompt, permission, no such file).
- Enable passwordless or cached sudo for the agent user (configure NOPASSWD in sudoers, or run sudo -v first to cache credentials).
- Run the agent over a connection that allows sudo (allocate a TTY, or use `sudo -n` in an environment with cached credentials).
- Verify the target path exists/is writable by root (parent directory present, filesystem not read-only or full).
Example fix
// before: failing because sudo cannot prompt for a password over a non-TTY channel raise RuntimeError(f"sudo tee failed: sudo: a terminal is required...") // after: grant the agent user passwordless tee for the target, or pre-cache credentials # /etc/sudoers.d/agent agent ALL=(root) NOPASSWD: /usr/bin/tee # or, before editing: sudo -v
Defensive patterns
Strategy: try-catch
Validate before calling
import subprocess
def sudo_available(path):
r = subprocess.run(["sudo", "-n", "true"], capture_output=True)
return r.returncode == 0
# also check: parent dir exists and sudo -n works before cmd_sudo_write Try / catch
try:
result = sudo_write(path, data)
except RuntimeError as e:
if "terminal is required" in str(e) or "a password is required" in str(e):
# prompt for credentials / run sudo -v, then retry
...
else:
log.error("sudo tee failed: %s", e) Prevention
- Configure NOPASSWD sudoers for the agent user or pre-cache credentials with sudo -v.
- Check `sudo -n true` succeeds before attempting sudo writes.
- Verify the target path and its parent directory exist and are root-writable.
When it happens
Trigger: Calling the remote sudo-write RPC (cmd_sudo_write in agent.py) when the spawned `sudo tee` process returns a non-zero exit code — e.g. sudo requires a password but no TTY/askpass is available (`sudo: a terminal is required`), the user is not in sudoers, the target path's directory doesn't exist or is unwritable, or the disk is full/read-only.
Common situations: Editing root-owned config files (e.g. /etc/...) over the remote agent session where the SSH connection has no TTY allocated, so sudo cannot prompt; sudo timeout expired mid-session; NOPASSWD not configured for the agent user; path typos or the file's parent directory was removed.
Understand the failure class
Background: "failed to write file", "Could not save figure", "Error saving remote file" — file write failed: causes and fixes across languages and libraries — this error's family across 38 libraries.
Related errors
AI-assisted analysis of sinelaw/fresh@67894ca546 (2026-09-13).
Data as JSON: /api/errors/8cbf3a525ea8a3e6.
Report an issue: GitHub.
Appendix: source
Thrown at crates/fresh-editor/src/services/remote/agent.py:137
path = validate_path(p["path"])
data = unb64(p["data"])
# Get original metadata to preserve permissions
mode = p.get("mode")
uid = p.get("uid")
gid = p.get("gid")
# Use sudo tee to write the file
proc = subprocess.Popen(
["sudo", "tee", path],
stdin=subprocess.PIPE,
stdout=subprocess.DEVNULL,
stderr=subprocess.PIPE,
)
_, stderr = proc.communicate(data)
if proc.returncode != 0:
raise RuntimeError(f"sudo tee failed: {stderr.decode().strip()}")
# Restore permissions and ownership if provided
if mode is not None:
subprocess.run(["sudo", "chmod", f"{mode:o}", path], check=True,
capture_output=True)
if uid is not None and gid is not None:
subprocess.run(["sudo", "chown", f"{uid}:{gid}", path], check=True,
capture_output=True)
send(id, r={"size": len(data)})
def cmd_stat(id, p):
"""Get file/directory metadata."""
path = validate_path(p["path"])
follow = p.get("link", True)
st = os.stat(path, follow_symlinks=follow)View on GitHub (pinned to 67894ca546)