tursodatabase/turso · error

has_hole is not supported for the given IO implementation

Error message

has_hole is not supported for the given IO implementation

What it means

has_hole() is an optional method on the IO trait whose default implementation panics. Only IO backends implementing sparse-file semantics can answer it; the sync engine uses it in partial-sync mode and core database code never calls it. Invoking it on a backend that did not override it crashes.

Source

Thrown at core/io/mod.rs:209

            if let Err(e) = self.pwrite(pos, buf.clone(), child_c) {
                c.abort();
                return Err(e);
            }
            pos += len as u64;
        }
        Ok(c)
    }
    fn size(&self) -> Result<u64>;
    fn truncate(&self, len: u64, c: Completion) -> Result<Completion>;

    /// Optional method implemented by the IO which supports "partial" files (e.g. file with "holes")
    /// This method is used in sync engine only for now (in partial sync mode) and never used in the core database code
    ///
    /// The hole is the contiguous file region which is not allocated by the file-system
    /// If there is a single byte which is allocated within a given range - method must return false in this case
    // todo: need to add custom completion type?
    fn has_hole(&self, _pos: usize, _len: usize) -> Result<bool> {
        panic!("has_hole is not supported for the given IO implementation")
    }
    /// Optional method implemented by the IO which supports "partial" files (e.g. file with "holes")
    /// This method is used in sync engine only for now (in partial sync mode) and never used in the core database code
    // todo: need to add custom completion type?
    fn punch_hole(&self, _pos: usize, _len: usize) -> Result<()> {
        panic!("punch_hole is not supported for the given IO implementation")
    }

    fn shared_wal_lock_byte(
        &self,
        _offset: u64,
        _exclusive: bool,
        _kind: SharedWalLockKind,
    ) -> Result<()> {
        Err(crate::LimboError::InternalError(
            "shared WAL coordination byte locking is not supported for this file".into(),
        ))
    }

View on GitHub (pinned to 6c72522679)

Solutions

  1. Use the file-backed platform IO (which implements hole queries) for partial sync
  2. Disable partial-sync mode and use full sync
  3. For custom IO: implement has_hole()/punch_hole() backed by SEEK_HOLE/fiemap or an allocation map

Example fix

// before
// partial sync over an IO without hole support -> panics in has_hole
engine.enable_partial_sync(memory_io);

// after
// use the file-backed platform IO that implements sparse-file queries
engine.enable_partial_sync(platform_file_io);
Defensive patterns

Strategy: fallback

Validate before calling

// enable partial sync only for IO backends known to implement hole queries
fn io_supports_holes(io: &std::sync::Arc<dyn IO>) -> bool {
    // match the concrete file-backed PlatformIO types you ship
    matches_downcast_to_platform_file_io(io)
}
if !io_supports_holes(&io) { engine.use_full_sync(); } else { engine.enable_partial_sync(io); }

Prevention

When it happens

Trigger: Enabling partial sync against an IO implementation that does not implement has_hole/punch_hole: in-memory IO, custom test IO backends, or platform variants without sparse-file support.

Common situations: Pointing the sync engine at a memory database in tests; shipping custom IO backends; filesystems lacking hole reporting (some network filesystems).

Related errors


AI-assisted analysis of tursodatabase/turso@6c72522679 (2026-08-20). Data as JSON: /api/errors/945ee81e3ac944cd. Report an issue: GitHub.