tokio-rs/tokio · error · io::Error
unexpected xucred size from LOCAL_PEERCRED
Error message
unexpected xucred size from LOCAL_PEERCRED
What it means
On Apple platforms, getsockopt(LOCAL_PEERCRED) writes an xucred struct; tokio asserts that the returned len equals size_of::<xucred>(). If the kernel wrote a different number of bytes (struct layout/size changed across OS versions), tokio returns InvalidData 'unexpected xucred size from LOCAL_PEERCRED'. A subsequent check also rejects unknown cr_version. It is a defensive guard against ABI drift in the BSD xucred structure.
Source
Thrown at tokio/src/net/unix/ucred.rs:252
unsafe {
let raw_fd = sock.as_raw_fd();
let mut xucred = MaybeUninit::<xucred>::zeroed();
let mut len = size_of::<xucred>() as socklen_t;
let ret = getsockopt(
raw_fd,
SOL_LOCAL,
LOCAL_PEERCRED,
xucred.as_mut_ptr() as *mut c_void,
&mut len,
);
if ret != 0 {
return Err(io::Error::last_os_error());
}
if len as usize != size_of::<xucred>() {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
"unexpected xucred size from LOCAL_PEERCRED",
));
}
let xucred = xucred.assume_init();
// Match `getpeereid(3)` and reject any `xucred` whose version we
// don't know how to interpret.
if xucred.cr_version != XUCRED_VERSION {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
"unexpected xucred version from LOCAL_PEERCRED",
));
}
// `cr_pid` is populated by the kernel since FreeBSD 13. PID 0 is
// the kernel scheduler and never a real userland peer, so weView on GitHub (pinned to 625954f365)
Solutions
- Rebuild tokio (and its socket2/libc deps) against the SDK matching the running kernel so size_of::<xucred>() matches.
- Upgrade tokio to a release that supports the current OS xucred layout, or downgrade the OS to a compatible version.
- Treat the error as non-fatal: fall back to getpeereid(3) or skip peer-credential checks on this platform.
- File an upstream issue if the size mismatch appears on a stable OS release — it may be a tokio/libc binding bug.
Example fix
// before
let cred = peer_cred.get(&stream).await?; // InvalidData on new macOS
// after
let cred = match peer_cred.get(&stream).await {
Ok(c) => Some(c),
Err(e) if e.kind() == io::ErrorKind::InvalidData => {
tracing::warn!("LOCAL_PEERCRED size mismatch: {e}; skipping peer creds");
None
}
Err(e) => return Err(e.into()),
}; Defensive patterns
Strategy: fallback
Validate before calling
// No portable pre-check; the size mismatch is an OS/ABI condition.
// Gate peer-cred calls behind a platform/version probe if you know the range:
#[cfg(all(unix, any(target_os = "macos", target_os = "freebsd")))]
fn try_peer_cred(stream: &UnixStream) -> Option<UCred> {
peer_cred.get(stream).await.ok()
} Type guard
fn is_xucred_size_mismatch(e: &io::Error) -> bool {
e.kind() == io::ErrorKind::InvalidData
&& e.to_string().contains("xucred size")
} Try / catch
match peer_cred.get(&stream).await {
Ok(c) => Ok(c),
Err(e) if e.to_string().contains("xucred size") => {
tracing::warn!("LOCAL_PEERCRED ABI mismatch; skipping peer creds: {e}");
Ok(fallback_cred())
}
Err(e) => Err(e.into()),
} Prevention
- Rebuild tokio and its deps against the SDK matching the running kernel after OS upgrades.
- Pin tokio to a release known to support the target macOS/BSD version, or vice versa.
- Treat peer-cred lookup as best-effort on BSD-derived platforms; don't hard-fail auth on it.
- Report persistent mismatches upstream — they may indicate a binding bug.
When it happens
Trigger: Calling UCred-related peer credential lookup (getsockopt LOCAL_PEERCRED) on macOS/FreeBSD where the kernel reports an xucred whose byte size differs from what tokio was compiled to expect. Most likely after a major OS upgrade that changed struct xucred.
Common situations: Major macOS/BSD upgrade that resized xucred (added/removed fields); running a tokio binary built against one SDK on a newer kernel; embedded/non-standard BSD derivative; cross-compilation toolchain ABI mismatch.
Related errors
- sender not available
- not a pipe
- not in O_WRONLY or O_RDWR access mode
- not in O_RDONLY or O_RDWR access mode
- listen cannot be called on a datagram socket
AI-assisted analysis of tokio-rs/tokio@625954f365 (2026-08-11).
Data as JSON: /api/errors/901eaa8aada37be8.
Report an issue: GitHub.