Schniz/fnm · critical

Can't get a temporary file

Error message

Can't get a temporary file

What it means

When a Node install artifact is a zip (the Windows distribution path), `Zip::extract_into` first streams the download into an unnamed temp file created by `tempfile::tempfile()` in the OS temp dir (TMPDIR/TEMP/TMP, else the system temp). Any io::Error from creating that file hits `.expect("Can't get a temporary file")` and panics, aborting the install mid-flight.

Source

Thrown at src/archive/zip.rs:22

use std::io::{self, Read};
use std::path::Path;
use tempfile::tempfile;
use zip::read::ZipArchive;

pub struct Zip<R: Read> {
    response: R,
}

impl<R: Read> Zip<R> {
    #[allow(dead_code)]
    pub fn new(response: R) -> Self {
        Self { response }
    }
}

impl<R: Read> Extract for Zip<R> {
    fn extract_into(mut self: Box<Self>, path: &Path) -> Result<(), Error> {
        let mut tmp_zip_file = tempfile().expect("Can't get a temporary file");

        debug!("Created a temporary zip file");
        io::copy(&mut self.response, &mut tmp_zip_file)?;
        debug!(
            "Wrote zipfile successfully. Now extracting into {}.",
            path.display()
        );

        let mut archive = ZipArchive::new(&mut tmp_zip_file)?;

        for i in 0..archive.len() {
            let mut file = archive.by_index(i)?;
            let outpath = path.join(file.mangled_name());

            {
                let comment = file.comment();
                if !comment.is_empty() {
                    debug!("File {i} comment: {comment}");

View on GitHub (pinned to 86adc9676c)

Solutions

  1. Point temp at a writable dir and retry: Windows `set TEMP=C:\Users\me\AppData\Local\Temp` / Unix `TMPDIR=/var/tmp fnm install 20`.
  2. Create the configured temp dir if missing: `mkdir -p "$TMPDIR"` (or recreate %TEMP%).
  3. Free space on the temp volume and raise tmpfs size or relocate it (`tmpfs` in /etc/fstab).
  4. If patching fnm: replace `.expect(...)` with `.map_err(|e| anyhow::anyhow!("can't create temp file: {e}"))?` so it reports instead of panicking.

Example fix

// before (src/archive/zip.rs)
let mut tmp_zip_file = tempfile().expect("Can't get a temporary file");

// after
let mut tmp_zip_file = tempfile()
    .map_err(|e| anyhow::anyhow!("Can't get a temporary file: {e}"))?;
Defensive patterns

Strategy: validation

Validate before calling

fn temp_dir_writable() -> bool {
    let dir = std::env::temp_dir();
    std::fs::create_dir_all(&dir).is_ok()
        && tempfile::tempfile().is_ok()
};
// also check free space: statvfs / GetDiskFreeSpaceEx before large installs

Try / catch

std::panic::catch_unwind(|| fnm_install(version)) // or invoke the binary
    .unwrap_or_else(|_| {
        eprintln!("install failed — check TMPDIR/TEMP writability and disk space");
        std::process::exit(1);
    });

Prevention

When it happens

Trigger: `fnm install <version>` (zip-based artifact, typically Windows) when the temp dir is missing, unwritable, full, or the process exhausted its file-descriptor quota — e.g. TMPDIR=/nonexistent, a full tmpfs /tmp, or a sandbox (Snap/Flatpak) denying writes to the default temp location.

Common situations: Docker/CI overriding TMPDIR to a path that was never created; small tmpfs /tmp filling during large downloads; TEMP pointing to a deleted per-user temp folder after cleanup tools ran; disk quota exhaustion on multi-tenant runners.

Related errors


AI-assisted analysis of Schniz/fnm@86adc9676c (2026-08-16). Data as JSON: /api/errors/9c934bac67724e23. Report an issue: GitHub.