{"record":{"id":"9c97e2278a18a6e5","repo":"rwf2/Rocket","slug":"brokenpipe","errorCode":"BrokenPipe","errorMessage":"spawn_block","messagePattern":"spawn_block","errorType":"exception","errorClass":"io::Error","httpStatus":null,"severity":"error","filePath":"core/lib/src/fs/temp_file.rs","lineNumber":176,"sourceCode":"    ///     file.persist_to(&some_path).await?;\n    ///     assert_eq!(file.path(), Some(&*some_path));\n    ///\n    ///     Ok(())\n    /// }\n    /// # let file = TempFile::Buffered { content: \"hi\".as_bytes() };\n    /// # rocket::async_test(handle(file)).unwrap();\n    /// ```\n    pub async fn persist_to<P>(&mut self, path: P) -> io::Result<()>\n        where P: AsRef<Path>\n    {\n        let new_path = path.as_ref().to_path_buf();\n        match self {\n            TempFile::File { path: either, .. } => {\n                let path = mem::replace(either, Either::Right(new_path.clone()));\n                match path {\n                    Either::Left(temp) => {\n                        let result = task::spawn_blocking(move || temp.persist(new_path)).await\n                            .map_err(|_| io::Error::new(io::ErrorKind::BrokenPipe, \"spawn_block\"))?;\n\n                        if let Err(e) = result {\n                            *either = Either::Left(e.path);\n                            return Err(e.error);\n                        }\n                    },\n                    Either::Right(prev) => {\n                        if let Err(e) = fs::rename(&prev, new_path).await {\n                            *either = Either::Right(prev);\n                            return Err(e);\n                        }\n                    }\n                }\n            }\n            TempFile::Buffered { content } => {\n                fs::write(&new_path, &content).await?;\n                *self = TempFile::File {\n                    file_name: None,","sourceCodeStart":158,"sourceCodeEnd":194,"githubUrl":"https://github.com/rwf2/Rocket/blob/3a54d079aef060a8f732bd04ea54b0581a604087/core/lib/src/fs/temp_file.rs#L158-L194","documentation":"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.","triggerScenarios":"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.","commonSituations":"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).","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"],"exampleFix":"# before (temp on tmpfs, target on disk → cross-device panic)\n# Rocket.toml defaults: temp_dir = /tmp\nfile.persist_to(\"/var/data/uploads/x.png\").await?;\n\n# after\n# Rocket.toml\n[default]\ntemp_dir = \"/var/data/tmp\"\n\nfile.persist_to(\"/var/data/uploads/x.png\").await?;","handlingStrategy":"try-catch","validationCode":"// ensure target dir exists and is on the same device as temp_dir before persisting\nfn can_persist(temp_dir: &Path, target: &Path) -> io::Result<()> {\n    std::fs::create_dir_all(target.parent().unwrap())?;\n    let a = temp_dir.metadata()?.dev(); // unix: use std::os::unix::fs::MetadataExt\n    let b = target.parent().unwrap().metadata()?.dev();\n    if a != b { return Err(io::Error::new(io::ErrorKind::CrossesDevices, \"temp and target on different filesystems\")); }\n    Ok(())\n}","typeGuard":null,"tryCatchPattern":"match file.persist_to(&dest).await {\n    Ok(()) => {}\n    Err(e) if e.kind() == io::ErrorKind::BrokenPipe => {\n        error_!(\"persist task died (shutdown or panic): {e}\");\n        return Status::InternalServerError;\n    }\n    Err(e) => { error_!(\"persist failed: {e}\"); return Status::InternalServerError; }\n}","preventionTips":["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"],"tags":["rust","rocket","tempfile","async","blocking-task","filesystem"],"backgroundTag":"blocking-task-panic","analyzedSha":"3a54d079aef060a8f732bd04ea54b0581a604087","analyzedAt":"2026-08-16T22:01:48.395Z","schemaVersion":2},"datasetVersion":"2026-08-16T23:17:17.608Z"}