GitoxideLabs/gitoxide · error

The tempfile with id

Error message

The tempfile with id {} wasn't available anymore

What it means

Returned by `Handle::close()` when the tempfile backing this handle is no longer present in the global registry. The registry entry is either gone (removed) or its slot holds `None`, which happens after the handle was already closed, taken, or the registry entry was consumed. The library throws this to prevent double-close/use-after-close on tempfile handles.

Solutions

  1. Ensure `close()` is called at most once per handle, e.g. via `Option<Handle>` and `.take()`
  2. Restructure code so ownership of the handle makes double-close impossible (move the handle into the closing scope)
  3. If persisting, use `persist()` instead of `close()` and do not reuse the handle afterwards

Example fix

// before
handle.close()?;
// ...
handle.close()?; // NotFound: already closed
// after
if let Some(handle) = handle_opt.take() {
    handle.close()?;
}
Defensive patterns

Strategy: try-catch

Validate before calling

// Rust: only close when the handle is still owned/open
fn close_once(handle: &mut Option<gix_tempfile::Handle<gix_tempfile::Closed>>) -> std::io::Result<()> {
    // a Closed-typed handle cannot call close(); None means already handled
    let _ = handle;
    Ok(())
}

Type guard

fn is_open(h: &Option<gix_tempfile::Handle<gix_tempfile::Open>>) -> bool { h.is_some() }

Try / catch

match handle.take() {
    Some(h) => match h.close() {
        Ok(_) => {},
        Err(e) if e.kind() == std::io::ErrorKind::NotFound => { /* already closed; treat as idempotent */ },
        Err(e) => return Err(e),
    },
    None => { /* already closed */ }
}

Prevention

When it happens

Trigger: Calling `close()` on a `Handle` that was already closed, or on a handle whose registry entry was consumed (e.g. via `take()`/persist), or after the registry entry was dropped.

Common situations: Closing the same tempfile handle twice in error-recovery paths; persisting a tempfile (which consumes it) and then calling `close()` on the stale handle; sharing a handle across threads where one side closes it first.

Understand the failure class

Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.

Related errors


AI-assisted analysis of GitoxideLabs/gitoxide@e73179060b (2026-09-08). Data as JSON: /api/errors/58b92f7c230e580c. Report an issue: GitHub.

Appendix: source

Thrown at gix-tempfile/src/handle.rs:191

        res.and_then(|(_k, v)| v.map(|v| v.into_tempfile().expect("correct runtime typing")))
    }

    /// Close the underlying file handle but keep track of the temporary file as before for automatic cleanup.
    ///
    /// This saves system resources in situations where one opens a tempfile file at a time, writes a new value, and closes
    /// it right after to perform more updates of this kind in other tempfiles. When all succeed, they can be renamed one after
    /// another.
    pub fn close(self) -> std::io::Result<Handle<Closed>> {
        match REGISTRY.remove(&self.id) {
            Some((id, Some(t))) => {
                std::mem::forget(self);
                expect_none(REGISTRY.insert(id, Some(t.close())));
                Ok(Handle::<Closed> {
                    id,
                    _marker: Default::default(),
                })
            }
            None | Some((_, None)) => Err(std::io::Error::new(
                std::io::ErrorKind::NotFound,
                format!("The tempfile with id {} wasn't available anymore", self.id),
            )),
        }
    }
}

/// Mutation
impl Handle<Writable> {
    /// Obtain a mutable handler to the underlying named tempfile and call `f(&mut named_tempfile)` on it.
    ///
    /// Note that for the duration of the call, a signal interrupting the operation will cause the tempfile not to be cleaned up
    /// as it is not visible anymore to the signal handler.
    ///
    /// # Assumptions
    /// The caller must assure that the signal handler for cleanup will be followed by an abort call so that
    /// this code won't run again on a removed instance. An error will occur otherwise.
    pub fn with_mut<T>(&mut self, once: impl FnOnce(&mut NamedTempFile) -> T) -> std::io::Result<T> {

View on GitHub (pinned to e73179060b)