rwf2/Rocket · error · io::Error
BrokenPipe
BrokenPipe
Error message
spawn_block
What it means
Runtime io error from TempFile::persist_to: the blocking call to NamedTempFile::persist was moved onto task::spawn_blocking, and awaiting the JoinHandle returned a JoinError (the blocking task panicked or the runtime cancelled it). Rocket maps that JoinError to io::ErrorKind::BrokenPipe with message 'spawn_block', surfacing it from persist_to before the persist result is even inspected.
Source
Thrown at core/lib/src/fs/temp_file.rs:176
/// file.persist_to(&some_path).await?;
/// assert_eq!(file.path(), Some(&*some_path));
///
/// Ok(())
/// }
/// # let file = TempFile::Buffered { content: "hi".as_bytes() };
/// # rocket::async_test(handle(file)).unwrap();
/// ```
pub async fn persist_to<P>(&mut self, path: P) -> io::Result<()>
where P: AsRef<Path>
{
let new_path = path.as_ref().to_path_buf();
match self {
TempFile::File { path: either, .. } => {
let path = mem::replace(either, Either::Right(new_path.clone()));
match path {
Either::Left(temp) => {
let result = task::spawn_blocking(move || temp.persist(new_path)).await
.map_err(|_| io::Error::new(io::ErrorKind::BrokenPipe, "spawn_block"))?;
if let Err(e) = result {
*either = Either::Left(e.path);
return Err(e.error);
}
},
Either::Right(prev) => {
if let Err(e) = fs::rename(&prev, new_path).await {
*either = Either::Right(prev);
return Err(e);
}
}
}
}
TempFile::Buffered { content } => {
fs::write(&new_path, &content).await?;
*self = TempFile::File {
file_name: None,View on GitHub (pinned to 3a54d079ae)
Solutions
- Make temp_dir and the persist target live on the same filesystem/device (set temp_dir in Rocket.toml to a dir on the target volume)
- Upgrade rocket (and thus tempfile) so cross-filesystem persist is handled by copy+delete instead of panicking
- Avoid issuing persist_to after initiating shutdown; drain in-flight uploads before dropping the runtime
- Inspect the original error: io error kinds here usually wrap a panic — check logs for the panic message above this error
Example fix
# before (temp on tmpfs, target on disk → cross-device panic)
# Rocket.toml defaults: temp_dir = /tmp
file.persist_to("/var/data/uploads/x.png").await?;
# after
# Rocket.toml
[default]
temp_dir = "/var/data/tmp"
file.persist_to("/var/data/uploads/x.png").await?; Defensive patterns
Strategy: try-catch
Validate before calling
// ensure target dir exists and is on the same device as temp_dir before persisting
fn can_persist(temp_dir: &Path, target: &Path) -> io::Result<()> {
std::fs::create_dir_all(target.parent().unwrap())?;
let a = temp_dir.metadata()?.dev(); // unix: use std::os::unix::fs::MetadataExt
let b = target.parent().unwrap().metadata()?.dev();
if a != b { return Err(io::Error::new(io::ErrorKind::CrossesDevices, "temp and target on different filesystems")); }
Ok(())
} Try / catch
match file.persist_to(&dest).await {
Ok(()) => {}
Err(e) if e.kind() == io::ErrorKind::BrokenPipe => {
error_!("persist task died (shutdown or panic): {e}");
return Status::InternalServerError;
}
Err(e) => { error_!("persist failed: {e}"); return Status::InternalServerError; }
} Prevention
- Put temp_dir on the same filesystem as the final upload destination
- Finish uploads before shutdown (grace period > worst upload time)
- Watch logs for the underlying blocking-task panic — BrokenPipe 'spawn_block' is only the symptom
When it happens
Trigger: Calling temp_file.persist_to("path").await when the blocking task panics — most commonly because the tempfile crate panics on a persist across filesystems/devices or an invalid target — or when the tokio runtime is shutting down and cancels the blocking task mid-await.
Common situations: Persisting to a path on a different mount than temp_dir (e.g. temp on tmpfs, target on disk) with a tempfile version lacking cross-device handling; calling persist_to during server shutdown; panics inside blocking threads triggered by extreme conditions (out of file descriptors, deleted temp dir).
Related errors
AI-assisted analysis of rwf2/Rocket@3a54d079ae (2026-08-16).
Data as JSON: /api/errors/9c97e2278a18a6e5.
Report an issue: GitHub.