{"record":{"id":"32970599da6a9994","repo":"can1357/oh-my-pi","slug":"could-not-create-temporary-file","errorCode":null,"errorMessage":"could not create temporary file","messagePattern":"could not create temporary file","errorType":"exception","errorClass":"io::Error","httpStatus":null,"severity":"error","filePath":"crates/pi-builtins/src/sponge.rs","lineNumber":182,"sourceCode":"\t\t.file_name()\n\t\t.unwrap_or_else(|| OsStr::new(\"sponge\"))\n\t\t.to_string_lossy();\n\tfor _ in 0..32 {\n\t\tlet nanos = SystemTime::now()\n\t\t\t.duration_since(UNIX_EPOCH)\n\t\t\t.map_or(0, |duration| duration.subsec_nanos() as u64);\n\t\tlet tag = nanos\n\t\t\t.wrapping_mul(0x9e37_79b9_7f4a_7c15)\n\t\t\t.wrapping_add(COUNTER.fetch_add(1, Ordering::Relaxed))\n\t\t\t.wrapping_add(std::process::id() as u64);\n\t\tlet path = dir.join(format!(\".{base}.sponge.{tag:016x}\"));\n\t\tmatch OpenOptions::new().write(true).create_new(true).open(&path) {\n\t\t\tOk(file) => return Ok((path, file)),\n\t\t\tErr(err) if err.kind() == io::ErrorKind::AlreadyExists => {},\n\t\t\tErr(err) => return Err(err),\n\t\t}\n\t}\n\tErr(io::Error::new(\n\t\tio::ErrorKind::AlreadyExists,\n\t\t\"could not create temporary file\",\n\t))\n}\n\n/// Creates the `sponge` builtin registration.\npub(crate) fn sponge_builtin<SE: ShellExtensions>() -> Registration<SE> {\n\tutil::<Sponge, SE>()\n}\n\n#[cfg(test)]\nmod tests {\n\tuse std::{ffi::OsString, fs};\n\n\tuse super::Sponge;\n\tuse crate::host::run_util;\n\n\t#[test]","sourceCodeStart":164,"sourceCodeEnd":200,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/crates/pi-builtins/src/sponge.rs#L164-L200","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before: absorbing into a read-only location\n$ 'cat dump.sql' > /usr/share/app/config.sql  # sponge target dir read-only\n// after\n$ 'cat dump.sql' > ~/work/config.sql","handlingStrategy":"try-catch","validationCode":"import * as fs from \"node:fs\";\n// verify the output file's directory is writable before sponge/replace_atomically\nconst dir = path.dirname(targetFile);\nfs.accessSync(dir, fs.constants.W_OK); // throws early with a clearer message","typeGuard":"const isWritableDir = (dir: string): boolean => {\n  try { fs.accessSync(dir, fs.constants.W_OK); return true; } catch { return false; }\n};","tryCatchPattern":"try {\n  await spongeWrite(targetFile, data);\n} catch (err) {\n  if (err instanceof Error && err.message === \"could not create temporary file\") {\n    // fall back to a writable directory or surface a permission hint\n    await spongeWrite(path.join(fallbackWritableDir(), path.basename(targetFile)), data);\n  } else throw err;\n}","preventionTips":["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."],"tags":["filesystem","sponge","atomic-write","io"],"backgroundTag":"temp-file-creation-failed","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}