denoland/deno · error · FsError
too many temp dirs exist
Error message
too many temp dirs exist
What it means
Deno.makeTempDirSync() tries up to MAX_TRIES=10 times to create a directory named dir + prefix + 8-hex-digit random + suffix (tmp_name uses a random u64), retrying only when creation fails with AlreadyExists. If every attempt collides with an existing path, the op gives up with io::ErrorKind::AlreadyExists 'too many temp dirs exist', wrapped with the context 'tmpdir'.
Source
Thrown at ext/fs/ops.rs:1175
for _ in 0..MAX_TRIES {
let path = tmp_name(&mut rng, &dir, prefix.as_deref(), suffix.as_deref())?;
// PERMISSIONS: this is ok because we verified the directory above
let path = CheckedPath::unsafe_new(Cow::Owned(path));
match fs.mkdir_sync(&path, false, Some(0o700)) {
Ok(_) => {
// PERMISSIONS: ensure the absolute path is not leaked
let path =
strip_dir_prefix(&dir, dir_arg.as_deref(), path.into_owned_path())?;
return path_into_string(path.into_os_string());
}
Err(FsError::Io(ref e)) if e.kind() == io::ErrorKind::AlreadyExists => {
continue;
}
Err(e) => return Err(e).context("tmpdir"),
}
}
Err(FsError::Io(io::Error::new(
io::ErrorKind::AlreadyExists,
"too many temp dirs exist",
)))
.context("tmpdir")
}
#[op2(stack_trace)]
#[string]
pub async fn op_fs_make_temp_dir_async(
state: Rc<RefCell<OpState>>,
#[string] dir_arg: Option<String>,
#[string] prefix: Option<String>,
#[string] suffix: Option<String>,
) -> Result<String, FsOpsError> {
let (dir, fs) =
make_temp_check_async(state, dir_arg.as_deref(), "Deno.makeTempDir()")?;
let mut rng = thread_rng();View on GitHub (pinned to 9ad36f7a2c)
Solutions
- Clean stale files matching the prefix/suffix pattern out of the target directory
- Pass a distinct prefix or suffix so generated names stop colliding
- Point dir at a fresh empty directory, or omit dir to use the OS temp dir
- As a last resort, catch AlreadyExists and retry with a varying prefix
Example fix
// before
Deno.makeTempDirSync({ dir: './tmp', prefix: 'job-', suffix: '.tmp' });
// after
Deno.makeTempDirSync({ dir: './tmp', prefix: `job-${Date.now()}-` }); Defensive patterns
Strategy: retry
Validate before calling
function cleanTempCollisions(dir: string, prefix: string, suffix: string): void {
try {
for (const e of Deno.readDirSync(dir)) {
const mid = e.name.slice(prefix.length, e.name.length - suffix.length);
if (e.name.startsWith(prefix) && e.name.endsWith(suffix) && /^[0-9a-f]{8,}$/.test(mid)) {
Deno.removeSync(`${dir}/${e.name}`, { recursive: true });
}
}
} catch { /* directory may not exist yet */ }
} Try / catch
for (let attempt = 0; attempt < 3; attempt++) {
try {
const dir = Deno.makeTempDirSync({ prefix: `job-${attempt}-` });
// use dir ...
break;
} catch (e) {
if (!(e instanceof Error) || !/too many temp dirs exist/.test(e.message)) throw e;
}
} Prevention
- Use a per-call unique prefix (timestamp/uuid fragment) so names cannot collide with leftovers
- Clean stale temp entries matching your prefix/suffix pattern
- In tests, make filesystem stubs return success or NotFound, never a blanket AlreadyExists
When it happens
Trigger: Deno.makeTempDirSync({ dir, prefix, suffix }) where all 10 generated names already exist: a directory deliberately pre-populated with files matching the prefix/suffix pattern, a test stub of the filesystem that always returns AlreadyExists, or a fixed/seeded RNG in tests producing identical names.
Common situations: Unit tests mocking Deno filesystem ops to always throw AlreadyExists; an adversarial or sync-agent process squatting matching names; leftover cleanup scripts that pre-create the exact pattern. With a real 64-bit random name space, natural collisions are practically impossible — this almost always means a stubbed FS or deliberate name squatting.
Related errors
- too many temp files exist
- ERR_FS_EISDIR
- '{}' is writable or readable by other users
- sockets cannot be copied
- the source path is not a valid file
AI-assisted analysis of denoland/deno@9ad36f7a2c (2026-08-20).
Data as JSON: /api/errors/d2461911c2b1ef35.
Report an issue: GitHub.