tursodatabase/turso · error

subjournal must be opened

Error message

subjournal must be opened

What it means

Pager::try_use_subjournal() acquires statement-level ownership of the subjournal (the in-memory before-image store used for statement rollback). It expects the subjournal to have been created first via open_subjournal(); all in-engine call sites pair them as open_subjournal()? then try_use_subjournal()?. Calling try before a successful open panics with "subjournal must be opened".

Source

Thrown at core/storage/pager.rs:2198

                    .write_offset
                    .fetch_add(page_size as u64 + 4, Ordering::SeqCst);
            })
        };
        let c = Completion::new_write(write_complete);

        let subjournal = self.subjournal.read();
        let subjournal = subjournal.as_ref().unwrap();

        let c = subjournal.write_page(write_offset, page_size, buffer, c)?;
        turso_assert!(c.succeeded(), "memory IO should complete immediately");
        Ok(())
    }

    /// try to "acquire" ownership on the subjournal of the connection-scoped pager
    /// if another statement owns the subjournal - return Busy error and let the caller retry attempt later
    pub fn try_use_subjournal(&self) -> Result<()> {
        let subjournal = self.subjournal.read();
        let subjournal = subjournal.as_ref().expect("subjournal must be opened");
        subjournal.try_use()
    }

    /// release ownership of the subjournal
    /// caller must guarantee that [Self::stop_use_subjournal] is called only after successful call to the [Self::try_use_subjournal]
    pub fn stop_use_subjournal(&self) {
        let subjournal = self.subjournal.read();
        let subjournal = subjournal.as_ref().expect("subjournal must be opened");
        subjournal.stop_use()
    }

    /// check if subjournal is in use for some statement
    pub fn subjournal_in_use(&self) -> bool {
        let subjournal = self.subjournal.read();
        let Some(subjournal) = subjournal.as_ref() else {
            return false;
        };
        subjournal.in_use()

View on GitHub (pinned to 492c4a71cd)

Solutions

  1. Call pager.open_subjournal()? immediately before pager.try_use_subjournal()?
  2. Never ignore the Result of open_subjournal - propagate errors before acquiring ownership
  3. In debug builds, assert the subjournal exists before acquiring

Example fix

// before
pager.try_use_subjournal()?; // panics: subjournal was never opened

// after
pager.open_subjournal()?;
pager.try_use_subjournal()?;
Defensive patterns

Strategy: validation

Validate before calling

// Acquire the subjournal in the documented order before use:
pager.open_subjournal()?;      // creates the in-memory subjournal
pager.try_use_subjournal()?;   // now safe: acquires statement ownership

Prevention

When it happens

Trigger: Code calling pager.try_use_subjournal() on a connection-scoped pager where open_subjournal() has not run, or where its Result was ignored and it failed before storing the Subjournal.

Common situations: Embedders driving the pager directly outside the VDBE flow, refactors that drop the open call on an error path, custom statement implementations.

Related errors


AI-assisted analysis of tursodatabase/turso@492c4a71cd (2026-08-20). Data as JSON: /api/errors/f9670f54265fabe0. Report an issue: GitHub.