can1357/oh-my-pi · error · io::Error
could not create temporary file
Error message
could not create temporary file
What it means
This io::Error is produced by sponge's create_sibling_temp (called from replace_atomically) when it exhausts candidate sibling temp-file names in the target directory without ever getting a successful create_new(true) open. Every attempt either collided (AlreadyExists, retried) or failed differently; after the loop it reports 'could not create temporary file' with ErrorKind::AlreadyExists. It means an atomic in-place replace of the target file could not even begin.
Source
Thrown at crates/pi-builtins/src/sponge.rs:182
.file_name()
.unwrap_or_else(|| OsStr::new("sponge"))
.to_string_lossy();
for _ in 0..32 {
let nanos = SystemTime::now()
.duration_since(UNIX_EPOCH)
.map_or(0, |duration| duration.subsec_nanos() as u64);
let tag = nanos
.wrapping_mul(0x9e37_79b9_7f4a_7c15)
.wrapping_add(COUNTER.fetch_add(1, Ordering::Relaxed))
.wrapping_add(std::process::id() as u64);
let path = dir.join(format!(".{base}.sponge.{tag:016x}"));
match OpenOptions::new().write(true).create_new(true).open(&path) {
Ok(file) => return Ok((path, file)),
Err(err) if err.kind() == io::ErrorKind::AlreadyExists => {},
Err(err) => return Err(err),
}
}
Err(io::Error::new(
io::ErrorKind::AlreadyExists,
"could not create temporary file",
))
}
/// Creates the `sponge` builtin registration.
pub(crate) fn sponge_builtin<SE: ShellExtensions>() -> Registration<SE> {
util::<Sponge, SE>()
}
#[cfg(test)]
mod tests {
use std::{ffi::OsString, fs};
use super::Sponge;
use crate::host::run_util;
#[test]View on GitHub (pinned to 9690622007)
Solutions
- Verify the output file's directory is writable: `touch <dir>/.sponge-probe` and check permissions (`ls -ld <dir>`).
- Write output to a different, writable directory (adjust the target path).
- Free space/inodes if the filesystem is full (`df -h <dir>`, `df -i <dir>`).
- If collisions are the cause, clear stale `<name>.tmp*`-style sibling files or retry after cleanup.
Example fix
// before: absorbing into a read-only location $ 'cat dump.sql' > /usr/share/app/config.sql # sponge target dir read-only // after $ 'cat dump.sql' > ~/work/config.sql
Defensive patterns
Strategy: try-catch
Validate before calling
import * as fs from "node:fs"; // verify the output file's directory is writable before sponge/replace_atomically const dir = path.dirname(targetFile); fs.accessSync(dir, fs.constants.W_OK); // throws early with a clearer message
Type guard
const isWritableDir = (dir: string): boolean => {
try { fs.accessSync(dir, fs.constants.W_OK); return true; } catch { return false; }
}; Try / catch
try {
await spongeWrite(targetFile, data);
} catch (err) {
if (err instanceof Error && err.message === "could not create temporary file") {
// fall back to a writable directory or surface a permission hint
await spongeWrite(path.join(fallbackWritableDir(), path.basename(targetFile)), data);
} else throw err;
} Prevention
- Check directory write permission before atomic in-place replaces.
- Avoid sponge targets on read-only or root-owned paths (/usr, /proc).
- Monitor free inodes/space on the target filesystem.
- Clean stale sibling temp files if the directory is known to accumulate them.
When it happens
Trigger: Calling the sponge builtin to absorb input into an output file whose directory is unwritable or read-only; a directory where every probed temp name collides (pathological/corrupt state); create_new failing due to permissions, exhausted inodes, or a full filesystem in ways other than AlreadyExists mapping into the final fallback.
Common situations: Writing into /usr, /proc, or other read-only mounts; saving into a directory owned by root without sudo; CI sandboxes with restricted write scopes; hitting the filesystem's file-count limit.
Related errors
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/32970599da6a9994.
Report an issue: GitHub.