{"record":{"id":"0ce74b73c21aa35d","repo":"can1357/oh-my-pi","slug":"could-not-persist-file","errorCode":null,"errorMessage":"could not persist file {}","messagePattern":"could not persist file (.+?)","errorType":"error_code","errorClass":"MkTempError","httpStatus":null,"severity":"critical","filePath":"crates/pi-builtins/src/mktemp.rs","lineNumber":54,"sourceCode":"static OPT_DRY_RUN: &str = \"dry-run\";\nstatic OPT_QUIET: &str = \"quiet\";\nstatic OPT_SUFFIX: &str = \"suffix\";\nstatic OPT_TMPDIR: &str = \"tmpdir\";\nstatic OPT_P: &str = \"p\";\nstatic OPT_T: &str = \"t\";\n\nstatic ARG_TEMPLATE: &str = \"template\";\n\n#[cfg(not(windows))]\nconst TMPDIR_ENV_VAR: &str = \"TMPDIR\";\n#[cfg(windows)]\nconst TMPDIR_ENV_VAR: &str = \"TMP\";\n\nconst FALLBACK_TMPDIR: &str = \"/tmp\";\n\n#[derive(Error, Debug)]\nenum MkTempError {\n\t#[error(\"could not persist file {}\", .0.quote())]\n\tPersist(PathBuf),\n\n\t#[error(\"with --suffix, template {} must end in X\", .0.quote())]\n\tMustEndInX(String),\n\n\t#[error(\"too few X's in template {}\", .0.quote())]\n\tTooFewXs(String),\n\n\t#[error(\"invalid template, {}, contains directory separator\", .0.quote())]\n\tPrefixContainsDirSeparator(String),\n\n\t#[error(\"invalid suffix {}, contains directory separator\", .0.quote())]\n\tSuffixContainsDirSeparator(String),\n\n\t#[error(\"invalid template, {}; with --tmpdir, it may not be absolute\", .0.quote())]\n\tInvalidTemplate(OsString),\n\n\t#[error(\"too many templates\")]","sourceCodeStart":36,"sourceCodeEnd":72,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/crates/pi-builtins/src/mktemp.rs#L36-L72","documentation":"MkTempError::Persist is raised by the builtin `mktemp` when the temporary file it created could not be persisted — i.e. the final write/rename/fsync of the created file under the target directory failed. The error carries the offending PathBuf so you can see which path failed. This typically indicates a filesystem-level problem rather than a template problem (template issues have their own variants).","triggerScenarios":"Creating a temp file in a directory that becomes read-only or full between creation and persistence; tmpdir on a filesystem that rejects the write; TMP env var pointing to a failing mount; disk-quota exhaustion mid-operation.","commonSituations":"/tmp filled to capacity on small root partitions; read-only remounts after errors; sandboxed CI runners with restricted write access to TMP; NFS mounts dropping writes.","solutions":["Check disk space (`df -h /tmp`) and free space or raise the quota","Verify TMP / --tmpdir points to a writable, non-read-only directory","Retry the operation — transient EIO/ENOSPC on the target filesystem may clear","Choose a different tmpdir on a healthier filesystem via --tmpdir","Check filesystem logs / dmesg for I/O errors on the underlying device"],"exampleFix":"// before\nTMP=/mnt/failing-nfs mktemp\n// after\nTMP=/tmp mktemp   // or pass --tmpdir=/var/tmp","handlingStrategy":"try-catch","validationCode":"const tmpDir = process.env.TMP ?? '/tmp';\nawait fs.access(tmpDir, fs.constants.W_OK);\nconst stat = await fs.statfs(tmpDir);\nif (stat.bavail * stat.bsize < 1_000_000) throw new Error('low disk space in tmpdir');","typeGuard":"const isWritableDir = async (p: string): Promise<boolean> => {\n  try { await fs.access(p, fs.constants.W_OK); return (await fs.stat(p)).isDirectory(); }\n  catch { return false; }\n};","tryCatchPattern":"try {\n  await Bun.$`mktemp ${tmpl}`.quiet();\n} catch (e) {\n  if (String(e).includes('could not persist file')) {\n    // fall back to an alternate tmpdir\n    await Bun.$`mktemp --tmpdir=/var/tmp ${tmpl}`.quiet();\n  } else throw e;\n}","preventionTips":["Monitor free space on the tmp filesystem","Prefer /var/tmp or an explicitly writable dir over fragile mounts","Clean stale temp files regularly","Handle ENOSPC/EIO as retryable-or-fallback conditions in scripts"],"tags":["filesystem","io","mktemp","tmpdir"],"backgroundTag":"temp-file-write-failed","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}