diesel-rs/diesel · error
Error closing SQLite blob
Error message
Error closing SQLite blob: {error_message} What it means
When an SqliteReadOnlyBlob (an incremental-blob-I/O handle) is dropped, diesel calls sqlite3_blob_close via close_inner; on failure it prints (while panicking) or panics with "Error closing SQLite blob: {message}". It means the blob handle could not be released, typically because the underlying connection or row is in a bad state.
Solutions
- Scope the blob tightly: read and finish with the blob before any transaction ends or the table is modified.
- Drop the blob before the connection (declare the connection binding after blob usage or use explicit blocks).
- Avoid writing to the same row/table while a read blob handle is open; re-open the blob after the write instead.
- If seen during a panic unwind, fix the originating panic — the blob message is a secondary cleanup diagnostic.
Example fix
// before
let conn = SqliteConnection::establish(url)?;
let blob = SqliteReadOnlyBlob::new(&conn, "t", "data", rowid, 0)?;
// ... table rewritten here -> close fails on drop
// after
let conn = SqliteConnection::establish(url)?;
{
let blob = SqliteReadOnlyBlob::new(&conn, "t", "data", rowid, 0)?;
// finish reading before any writes to the row
} Defensive patterns
Strategy: type-guard
Validate before calling
let blob = SqliteReadOnlyBlob::new(&conn, "t", "data", rowid, 0)?; // read immediately, then drop(blob) before commit/write
Type guard
fn blob_scope_safe<T>(conn: &SqliteConnection, f: impl FnOnce() -> T) -> T {
// run all blob work inside one scope; blob cannot outlive this call
f()
} Try / catch
std::panic::catch_unwind(AssertUnwindSafe(|| drop(blob))) // on cleanup paths where a second panic must be tolerated
Prevention
- Keep blob handles within a small lexical scope; drop before transaction end.
- Don't update/rewrite rows while a read blob handle is open.
- Read blob data fully into memory if you need it past the statement's lifetime.
When it happens
Trigger: Dropping an SqliteReadOnlyBlob whose close_inner() returns Err — e.g. the underlying connection was invalidated, the row holding the blob was freed, or the blob was already closed/failed mid-read; during normal drop it panics, during unwinding it logs to stderr.
Common situations: Reading a blob from a row while another statement modifies/rewrites the table (incremental blob handles point into the row); holding blob handles across transaction commit/rollback; dropping connection and blob in the wrong order.
Related errors
- Error closing SQLite connection
- DIESEL_MAX_COLUMN_COUNT is a number that fits into a u16
- Unable to perform MySQL global initialization
- Using window functions in WHERE clauses is not supported
- Sqlite's documentation state that this case
AI-assisted analysis of diesel-rs/diesel@6fa6ed01b2 (2026-09-07).
Data as JSON: /api/errors/368b26988d7c04f8.
Report an issue: GitHub.
Appendix: source
Thrown at diesel/src/sqlite/connection/sqlite_blob.rs:37
#[cfg_attr(not(feature = "std"), expect(dead_code))]
pub struct SqliteReadOnlyBlob<'conn> {
pub(crate) blob: core::ptr::NonNull<ffi::sqlite3_blob>,
pub(crate) read_index: usize,
pub(crate) blob_size: usize,
pub(crate) _pd: core::marker::PhantomData<&'conn mut ffi::sqlite3_blob>,
}
impl Drop for SqliteReadOnlyBlob<'_> {
fn drop(&mut self) {
use crate::util::std_compat::panicking;
if let Err(error_message) = self.close_inner() {
if panicking() {
#[cfg(feature = "std")]
eprintln!("Error closing SQLite blob: {error_message}");
} else {
panic!("Error closing SQLite blob: {error_message}");
}
}
}
}
impl SqliteReadOnlyBlob<'_> {
/// Is the blob storage empty
pub fn is_empty(&self) -> bool {
self.len() == 0
}
/// The size of the underlying blob in bytes
pub fn len(&self) -> usize {
self.blob_size
}
/// Close the handle
///View on GitHub (pinned to 6fa6ed01b2)