astrid-runtime/astrid · error
IPC write timed out
Error message
IPC write timed out
What it means
write_message wraps the framed write (length prefix, payload, flush) in tokio::time::timeout(WRITE_TIMEOUT). If the write does not complete within that window, the library converts the elapsed future into a TimedOut io::Error with this message. It indicates the IPC peer or OS pipe/socket is not draining writes fast enough.
Solutions
- Investigate why the peer is not reading: check for a blocked or deadlocked read loop on the receiving side
- Increase WRITE_TIMEOUT if payloads are legitimately large or the host is slow
- Add backpressure/queueing in forward_outbound instead of blocking on a stalled peer
- Monitor and restart the unresponsive peer process
Example fix
// before const WRITE_TIMEOUT: Duration = Duration::from_secs(5); // after: allow larger frames more time const WRITE_TIMEOUT: Duration = Duration::from_secs(30);
Defensive patterns
Strategy: retry
Validate before calling
// pre-check: is the peer still responsive?
let peer_alive = !child_try_wait()?.matches(&ExitStatus::default()); // or poll a ping/pong
if !peer_alive { restart_peer()?; } Try / catch
match write_message(&mut writer, &payload).await {
Err(e) if e.kind() == io::ErrorKind::TimedOut => {
// peer stalled: attempt bounded retry, then recycle the peer
if retries < MAX_RETRIES { retry_after_backoff().await?; }
else { restart_peer().await?; }
}
other => other?,
} Prevention
- Keep the peer's read loop always draining its IPC input
- Size WRITE_TIMEOUT for your largest legitimate payload plus slack
- Add heartbeats to detect stalled peers early
- Avoid blocking the tokio runtime with heavy work between reads/writes
When it happens
Trigger: forward_outbound pushes messages to a peer whose read loop is stalled or blocked; the peer process is paused (SIGSTOP), deadlockeds, or its receive buffer is full; the socket/pipe is congested with a large backlog; WRITE_TIMEOUT is too small for very large payloads on slow media.
Common situations: Deadlocked child process not reading its IPC stdin/socket; very large messages exceeding the timeout budget on a loaded machine; peer stopped by a debugger breakpoint; system under heavy load starving the tokio runtime.
Understand the failure class
Background: Request timed out: what client-side request timeouts mean across libraries (Request timed out, TIMED_OUT, APITimeoutError) — this error's family across 39 libraries.
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- connection timed out after 5s
- Admin request timed out after
- an Astrid daemon appears to be running but its uplink is…
- daemon request
- daemon response timed out after 5s
AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09).
Data as JSON: /api/errors/9bf1a4b280f3fb8d.
Report an issue: GitHub.
Appendix: source
Thrown at crates/astrid-uplink/src/native/mod.rs:546
frame
.as_object_mut()
.expect("wire frame is an object")
.insert(
"principal".to_owned(),
serde_json::Value::String(principal.clone()),
);
}
let bytes = serde_json::to_vec(&frame)
.map_err(|error| std::io::Error::other(format!("serialize IPC message: {error}")))?;
let len = u32::try_from(bytes.len())
.map_err(|_| std::io::Error::other("IPC message exceeds 4 GiB"))?;
tokio::time::timeout(WRITE_TIMEOUT, async {
writer.write_all(&len.to_be_bytes()).await?;
writer.write_all(&bytes).await?;
writer.flush().await
})
.await
.map_err(|_| std::io::Error::new(std::io::ErrorKind::TimedOut, "IPC write timed out"))?
}
fn publish_lifecycle(event_bus: &EventBus, topic: Topic, principal: &str, reason: Option<&str>) {
let payload = match reason {
Some(reason) => IpcPayload::Disconnect {
reason: Some(reason.to_owned()),
},
None => IpcPayload::Connect,
};
let message = IpcMessage::new(topic, payload, uuid::Uuid::nil()).with_principal(principal);
event_bus.publish(AstridEvent::Ipc {
metadata: EventMetadata::new(EVENT_SOURCE),
message,
});
}
View on GitHub (pinned to affd8760f4)