{"record":{"id":"3b170d137db928a6","repo":"vercel/turborepo","slug":"daemon-peer-uid-peer-uid-does-not-match-current","errorCode":null,"errorMessage":"daemon peer uid {peer_uid} does not match current uid {current_uid}","messagePattern":"daemon peer uid (.+?) does not match current uid (.+?)","errorType":"exception","errorClass":"std::io::Error","httpStatus":null,"severity":"error","filePath":"crates/turborepo-daemon/src/endpoint.rs","lineNumber":539,"sourceCode":"#[cfg(unix)]\nfn set_private_socket_permissions(sock_path: &AbsoluteSystemPath) -> Result<(), std::io::Error> {\n    use std::os::unix::fs::PermissionsExt;\n\n    std::fs::set_permissions(\n        sock_path.as_std_path(),\n        std::fs::Permissions::from_mode(PRIVATE_SOCKET_MODE),\n    )\n}\n\n#[cfg(unix)]\npub(crate) fn authorize_peer(stream: &tokio::net::UnixStream) -> Result<(), std::io::Error> {\n    let peer_uid = stream.peer_cred()?.uid();\n    let current_uid = current_uid();\n\n    if peer_uid == current_uid {\n        Ok(())\n    } else {\n        Err(std::io::Error::new(\n            std::io::ErrorKind::PermissionDenied,\n            format!(\"daemon peer uid {peer_uid} does not match current uid {current_uid}\"),\n        ))\n    }\n}\n\n#[cfg(unix)]\nstruct AuthorizedUnixStream(tokio::net::UnixStream);\n\n#[cfg(unix)]\nimpl AuthorizedUnixStream {\n    fn project(self: std::pin::Pin<&mut Self>) -> std::pin::Pin<&mut tokio::net::UnixStream> {\n        unsafe { self.map_unchecked_mut(|s| &mut s.0) }\n    }\n}\n\n#[cfg(unix)]\nimpl AsyncRead for AuthorizedUnixStream {","sourceCodeStart":521,"sourceCodeEnd":557,"githubUrl":"https://github.com/vercel/turborepo/blob/f9245100cf0d31d96628804ead485f6bf226e55a/crates/turborepo-daemon/src/endpoint.rs#L521-L557","documentation":"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.","triggerScenarios":"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.","commonSituations":"`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","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"],"exampleFix":"# bash: don't inherit turbo env across users\nsudo -E env -u TURBO_DAEMON_PID_FILE -u TURBO_DAEMON_SOCKET turbo run build\n# or run the whole command as the daemon's user","handlingStrategy":"validation","validationCode":"// client side: verify the socket belongs to your uid before talking to it\nlet m = std::fs::metadata(sock_path)?;\nuse std::os::unix::fs::MetadataExt;\nif m.uid() != unsafe { libc::geteuid() } { anyhow::bail!(\"foreign daemon socket\"); }","typeGuard":null,"tryCatchPattern":"// daemon side this is by design; on the client treat PermissionDenied\n// 'peer uid' as 'wrong daemon' — unset overrides and discover your own:\nErr(e) if e.to_string().contains(\"peer uid\") => { reset_turbo_env(); reconnect()?; }","preventionTips":["Use `sudo -E` sparingly with TURBO_* env present","One daemon per uid; never share socket paths across accounts or containers"],"tags":["daemon","unix","security","peer-credentials","sock-peercred"],"backgroundTag":"peer-uid-mismatch","analyzedSha":"f9245100cf0d31d96628804ead485f6bf226e55a","analyzedAt":"2026-08-17T10:46:15.696Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}