rwf2/Rocket · error · io::Error
Other
Other
Error message
spawn_block panic
What it means
Runtime io error while materializing an incoming multipart file part (TempFile::internal_from): creating the NamedTempFile happens inside task::spawn_blocking, and the JoinHandle returned a JoinError. Rocket maps it to io::ErrorKind::Other with message 'spawn_block panic'. In practice this is almost always the blocking task being cancelled at runtime shutdown rather than a literal panic in tempfile creation.
Source
Thrown at core/lib/src/fs/temp_file.rs:521
}
}
async fn from<'a>(
req: &Request<'_>,
data: Data<'_>,
file_name: Option<&'a FileName>,
content_type: Option<ContentType>,
) -> io::Result<Capped<TempFile<'a>>> {
let limit = content_type.as_ref()
.and_then(|ct| ct.extension())
.and_then(|ext| req.limits().find(["file", ext.as_str()]))
.or_else(|| req.limits().get("file"))
.unwrap_or(Limits::FILE);
let temp_dir = req.rocket().config().temp_dir.relative();
let file = task::spawn_blocking(move || NamedTempFile::new_in(temp_dir));
let file = file.await;
let file = file.map_err(|_| io::Error::new(io::ErrorKind::Other, "spawn_block panic"))??;
let (file, temp_path) = file.into_parts();
let mut file = File::from_std(file);
let fut = data.open(limit).stream_to(tokio::io::BufWriter::new(&mut file));
let n = fut.await;
let n = n?;
let temp_file = TempFile::File {
content_type, file_name,
path: Either::Left(temp_path),
len: n.written,
};
Ok(Capped::new(temp_file, n))
}
}
#[crate::async_trait]
impl<'v> FromFormField<'v> for Capped<TempFile<'v>> {View on GitHub (pinned to 3a54d079ae)
Solutions
- Raise shutdown grace so uploads finish: Rocket.toml [default.shutdown] grace = 30, timeout is separate
- Drain/stop accepting uploads before shutdown (e.g. readiness gate in orchestrators)
- Check dmesg/logs for resource exhaustion if shutdown is not the cause
- Retry the upload client-side on network error — this failure manifests to the client as a dropped connection
Example fix
# before # Rocket.toml (default grace = 5s, too short for slow uploads) # after # Rocket.toml [default.shutdown] grace = 30 ctrlc = true signals = ["term"]
Defensive patterns
Strategy: try-catch
Validate before calling
// fail fast at startup if temp_dir is unusable, instead of per-request
# use std::path::Path;
fn temp_dir_ok(temp_dir: &Path) -> io::Result<()> {
std::fs::create_dir_all(temp_dir)?;
let probe = tempfile::Builder::new().prefix("probe").tempfile_in(temp_dir)?;
drop(probe);
Ok(())
} Try / catch
// in the handler; internal_from errors surface as a failed TempFile guard
// register a catcher that distinguishes shutdown noise from real I/O failure
#[catch(500)]
fn internal(r: &Request) -> &'static str {
error_!("temp file creation failed (spawn_block panic?): {e:?}", e = r.guard::<&std::io::Error>());
"internal error"
} Prevention
- Set temp_dir to a writable, same-filesystem directory in Rocket.toml for production
- Make shutdown.grace longer than max upload duration, and stop accepting uploads first
- Clients should retry uploads that die mid-stream — this failure severs the connection
When it happens
Trigger: A multipart upload with a file part arrives exactly as the server shuts down (grace period expires and the runtime cancels blocking tasks), so the spawn_blocking future is dropped/JoinError; also possible if the process is critically out of resources and the task panics.
Common situations: Deployments that recycle workers mid-upload (Kubernetes rolling updates, systemd restarts); load tests that shut servers down while uploads are in flight; very long uploads with a short shutdown.grace.
Related errors
AI-assisted analysis of rwf2/Rocket@3a54d079ae (2026-08-16).
Data as JSON: /api/errors/3ff6e4b39204ea67.
Report an issue: GitHub.