benbjohnson/litestream · warning

temp file not tracked

Error message

temp file not tracked

What it means

errTempFileNotFound is the sentinel error indicating a requested temp file name is not present in the VFS's in-memory temp-file registry. Delete translates it into the opaque 'cannot delete vfs file' error, and the Delete/deleteTempFile path returns it when the name is tracked-format (temp-style name) but no corresponding tracked temp file entry exists.

Source

Thrown at vfs.go:517

}

func openFlagToOSFlag(flag sqlite3vfs.OpenFlag) int {
	var v int
	if flag&sqlite3vfs.OpenReadWrite != 0 {
		v |= os.O_RDWR
	} else if flag&sqlite3vfs.OpenReadOnly != 0 {
		v |= os.O_RDONLY
	}
	if flag&sqlite3vfs.OpenCreate != 0 {
		v |= os.O_CREATE
	}
	if flag&sqlite3vfs.OpenExclusive != 0 {
		v |= os.O_EXCL
	}
	return v
}

var errTempFileNotFound = fmt.Errorf("temp file not tracked")

// localTempFile fulfills sqlite3vfs.File solely for SQLite temp & transient files.
// These files stay on the local filesystem and optionally delete themselves
// when SQLite closes them (DeleteOnClose flag).
type localTempFile struct {
	f             *os.File
	deleteOnClose bool
	lockType      atomic.Int32
	onClose       func()
}

func newLocalTempFile(f *os.File, deleteOnClose bool, onClose func()) *localTempFile {
	return &localTempFile{f: f, deleteOnClose: deleteOnClose, onClose: onClose}
}

func (tf *localTempFile) Close() error {
	err := tf.f.Close()
	if tf.deleteOnClose {

View on GitHub (pinned to 4ed7a308f6)

Solutions

  1. Ensure the same VFS instance performs open and delete; avoid recreating the VFS between operations.
  2. Treat the file as already gone if DeleteOnClose removed it; check the registry presence before deleting programmatically.
  3. Remove the underlying local file directly (its hashed temp path) if you only need the bytes gone.
  4. If this recurs under normal SQLite operation, report it — SQLite should only delete files it opened.

Example fix

// before
db.Close()
vfs.Delete(name, false) // already untracked by DeleteOnClose

// after
db.Close() // DeleteOnClose already removed the temp file; skip manual delete
Defensive patterns

Strategy: try-catch

Validate before calling

// check registry membership before delete, if the API exposes it
if !vfs.TracksTempFile(name) { return nil }

Try / catch

if errors.Is(err, errTempFileNotFound) || strings.Contains(err.Error(), "cannot delete vfs file") {
    // treat as already-deleted; optionally os.Remove local temp path
}

Prevention

When it happens

Trigger: xDelete on a temp-style name that was never opened via this VFS, was already deleted (registry entry removed, e.g. by DeleteOnClose), or after a restart when the registry is empty; also from deleteTempFile's cleanup path when a lookup misses.

Common situations: Double-delete of the same temp journal; connection closed with DeleteOnClose removing the mapping before SQLite issues xDelete; multiple VFS instances where the file was opened on a different instance.

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 benbjohnson/litestream@4ed7a308f6 (2026-09-06). Data as JSON: /api/errors/75be7c2b1c316318. Report an issue: GitHub.