vercel/turborepo · error · std::io::Error
daemon peer uid {peer_uid} does not match current uid {curre
Error message
daemon peer uid {peer_uid} does not match current uid {current_uid} What it means
authorize_peer (endpoint.rs:539) runs on the daemon after accepting a Unix socket connection: it reads the peer's credentials via SO_PEERCRED (stream.peer_cred()) and requires peer uid == current euid. This PermissionDenied error means a process running as a different uid connected to the daemon socket; the daemon rejects it to prevent cross-user task/result injection.
Source
Thrown at crates/turborepo-daemon/src/endpoint.rs:539
#[cfg(unix)]
fn set_private_socket_permissions(sock_path: &AbsoluteSystemPath) -> Result<(), std::io::Error> {
use std::os::unix::fs::PermissionsExt;
std::fs::set_permissions(
sock_path.as_std_path(),
std::fs::Permissions::from_mode(PRIVATE_SOCKET_MODE),
)
}
#[cfg(unix)]
pub(crate) fn authorize_peer(stream: &tokio::net::UnixStream) -> Result<(), std::io::Error> {
let peer_uid = stream.peer_cred()?.uid();
let current_uid = current_uid();
if peer_uid == current_uid {
Ok(())
} else {
Err(std::io::Error::new(
std::io::ErrorKind::PermissionDenied,
format!("daemon peer uid {peer_uid} does not match current uid {current_uid}"),
))
}
}
#[cfg(unix)]
struct AuthorizedUnixStream(tokio::net::UnixStream);
#[cfg(unix)]
impl AuthorizedUnixStream {
fn project(self: std::pin::Pin<&mut Self>) -> std::pin::Pin<&mut tokio::net::UnixStream> {
unsafe { self.map_unchecked_mut(|s| &mut s.0) }
}
}
#[cfg(unix)]
impl AsyncRead for AuthorizedUnixStream {View on GitHub (pinned to f9245100cf)
Solutions
- Run turbo as the same user that owns the daemon (no mixed sudo/su for the same cache paths)
- Unset TURBO daemon/socket env overrides when switching users: `env -u TURBO_DAEMON... `
- Restart a per-user daemon instead of sharing one socket across accounts
Example fix
# bash: don't inherit turbo env across users sudo -E env -u TURBO_DAEMON_PID_FILE -u TURBO_DAEMON_SOCKET turbo run build # or run the whole command as the daemon's user
Defensive patterns
Strategy: validation
Validate before calling
// client side: verify the socket belongs to your uid before talking to it
let m = std::fs::metadata(sock_path)?;
use std::os::unix::fs::MetadataExt;
if m.uid() != unsafe { libc::geteuid() } { anyhow::bail!("foreign daemon socket"); } Try / catch
// daemon side this is by design; on the client treat PermissionDenied
// 'peer uid' as 'wrong daemon' — unset overrides and discover your own:
Err(e) if e.to_string().contains("peer uid") => { reset_turbo_env(); reconnect()?; } Prevention
- Use `sudo -E` sparingly with TURBO_* env present
- One daemon per uid; never share socket paths across accounts or containers
When it happens
Trigger: A different user on the same machine connects to your daemon socket — e.g. TURBO daemon env vars (socket path) leaked into another account's session via sudo/su, shared shell profiles, or a socket path on a shared volume used by multiple containers.
Common situations: `sudo -E` / `su` into another account while retaining TURBO_* env Multi-tenant CI boxes sharing HOME or XDG dirs Containers sharing a volume where the socket lives, with different uid mappings
Related errors
- daemon socket parent is owned by another user: {socket_dir}
- daemon socket path is owned by another user: {path}
- owner-only security descriptor did not contain a DACL
- daemon socket path did not have an owner SID
- daemon socket parent is not a directory: {socket_dir}
AI-assisted analysis of vercel/turborepo@f9245100cf (2026-08-17).
Data as JSON: /api/errors/3b170d137db928a6.
Report an issue: GitHub.