rustdesk/rustdesk · error · CliprdrError

file handle not found

Error message

file handle not found

What it means

LocalFile::read_exact_at first calls load_handle() to (re)open the backing file, then expects self.handle to be Some. If after load_handle the handle is still None, it cannot satisfy the positional read and returns CliprdrError::FileError wrapping io::ErrorKind::NotFound with 'file handle not found' for the file's path.

Source

Thrown at libs/clipboard/src/platform/unix/local_file.rs:230

                err: e,
            })?;
            let mut reader = BufReader::with_capacity(BLOCK_SIZE as usize * 2, handle);
            reader.fill_buf().map_err(|e| CliprdrError::FileError {
                path: self.path.to_string_lossy().to_string(),
                err: e,
            })?;
            self.handle = Some(reader);
        };
        Ok(())
    }

    pub fn read_exact_at(&mut self, buf: &mut [u8], offset: u64) -> Result<(), CliprdrError> {
        self.load_handle()?;

        let Some(handle) = self.handle.as_mut() else {
            return Err(CliprdrError::FileError {
                path: self.path.to_string_lossy().to_string(),
                err: std::io::Error::new(std::io::ErrorKind::NotFound, "file handle not found"),
            });
        };

        let read_result = if offset != self.offset.load(Ordering::Relaxed) {
            handle
                .seek(std::io::SeekFrom::Start(offset))
                .and_then(|_| handle.read_exact(buf))
        } else {
            handle.read_exact(buf)
        };
        if let Err(e) = read_result {
            return Err(self.invalidate_handle(e));
        }
        let new_offset = offset + (buf.len() as u64);
        self.offset.store(new_offset, Ordering::Relaxed);

        // gc file handle
        if new_offset >= self.size {

View on GitHub (pinned to 91c9fccbb0)

Solutions

  1. Check the file still exists and is a regular file before starting the transfer
  2. Call load_handle()/open explicitly and verify it returns a handle before reading
  3. Retry the operation after re-creating the LocalFile for the path
  4. Verify the path is valid on the local side (not a stale remote descriptor)

Example fix

// before
self.load_handle()?;
// after: fail early if handle could not be established
self.load_handle()?;
if self.handle.is_none() {
    return Err(CliprdrError::FileError {
        path: self.path.to_string_lossy().to_string(),
        err: std::io::Error::new(std::io::ErrorKind::NotFound, "file handle not found"),
    });
}
Defensive patterns

Strategy: validation

Validate before calling

// before transferring, verify the local file is readable
let p = std::path::Path::new(&path);
if !p.is_file() {
    return Err(anyhow!("file missing before transfer: {}", path));
}

Try / catch

match file.read_exact_at(&mut buf, offset) {
    Ok(()) => {},
    Err(CliprdrError::FileError { path, err }) if err.kind() == std::io::ErrorKind::NotFound => {
        // re-create LocalFile for `path` and retry once
    }
    Err(e) => return Err(e.into()),
}

Prevention

When it happens

Trigger: Calling read_exact_at on a LocalFile whose load_handle() succeeded but left self.handle unset — i.e. the underlying open path produced no handle, or the handle was cleared concurrently/between calls and load_handle did not re-open it.

Common situations: Reading a clipboard file that was deleted or became inaccessible between listing and download; the file being a special file that opens differently; a race where close/release runs while a read is in flight.

Understand the failure class

Background: "File not found" and ENOENT errors: why libraries can't find a file that should exist — this error's family across 50 libraries.

Related errors


AI-assisted analysis of rustdesk/rustdesk@91c9fccbb0 (2026-09-10). Data as JSON: /api/errors/72dfd2e0a9dedd1b. Report an issue: GitHub.