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

  1. Verify the output file's directory is writable: `touch <dir>/.sponge-probe` and check permissions (`ls -ld <dir>`).
  2. Write output to a different, writable directory (adjust the target path).
  3. Free space/inodes if the filesystem is full (`df -h <dir>`, `df -i <dir>`).
  4. 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

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.