benbjohnson/litestream · error
litestream_lag is read-only
Error message
litestream_lag is read-only
What it means
The litestream_lag PRAGMA is read-only: FileControl() in vfs.go returns this error when pragmaValue is non-nil, i.e. the caller executed `PRAGMA litestream_lag = <value>`. Reading it returns the seconds since the last successful replica poll ('-1' if never polled successfully).
Source
Thrown at vfs.go:2447
return nil, fmt.Errorf("unsupported file control op: %d", op)
}
name := strings.ToLower(pragmaName)
f.logger.Debug("file control", "pragma", name, "value", pragmaValue)
switch name {
case "litestream_txid":
if pragmaValue != nil {
return nil, fmt.Errorf("litestream_txid is read-only")
}
txid := f.Pos().TXID
result := txid.String()
return &result, nil
case "litestream_lag":
if pragmaValue != nil {
return nil, fmt.Errorf("litestream_lag is read-only")
}
lastPoll := f.LastPollSuccess()
if lastPoll.IsZero() {
result := "-1" // Never polled successfully
return &result, nil
}
lag := int64(time.Since(lastPoll).Seconds())
result := strconv.FormatInt(lag, 10)
return &result, nil
case "litestream_time":
if pragmaValue == nil {
result := f.currentTimeString()
return &result, nil
}
if strings.EqualFold(*pragmaValue, "latest") {
if err := f.ResetTime(context.Background()); err != nil {View on GitHub (pinned to 4ed7a308f6)
Solutions
- Remove the assignment; use the read form `PRAGMA litestream_lag;`
- Wait for replication to catch up or trigger a replication sync through litestream's API/CLI to lower actual lag
- For monitoring, poll the read form and alert based on the returned seconds (-1 means never polled)
- Catch the error and explain that lag is a computed metric, not a setting
Example fix
// before PRAGMA litestream_lag = 0; -- attempt to reset lag // after PRAGMA litestream_lag; -- read current lag in seconds (-1 = never polled) // to reduce lag, trigger replication: // litestream replicate ... / use SyncAndWait() from the Go API
Defensive patterns
Strategy: validation
Validate before calling
// Reject assignment form before executing
if strings.Contains(strings.ToLower(stmt), "litestream_lag=") ||
strings.Contains(strings.ToLower(stmt), "litestream_lag =") {
return errors.New("litestream_lag is read-only; use the read form PRAGMA litestream_lag;")
} Try / catch
_, err := db.Exec("PRAGMA litestream_lag = ?", v)
if err != nil && strings.Contains(err.Error(), "litestream_lag is read-only") {
// fall back to reading current lag
row := db.QueryRow("PRAGMA litestream_lag")
var lag string
_ = row.Scan(&lag)
} Prevention
- Only query litestream_lag; treat it as a computed monitoring metric
- Reduce real lag by triggering replication sync (SyncAndWait / CLI), not by writing the pragma
- Treat a return of '-1' as 'never polled successfully' and alert on replica connectivity
- Block write-forms of litestream_* pragmas in shared DB helper code
When it happens
Trigger: Executing `PRAGMA litestream_lag = '0'` or any assignment form, typically in an attempt to zero out or adjust the reported replication lag. The non-nil pragmaValue triggers the read-only guard.
Common situations: Developers assume lag is tunable (e.g. to force immediate sync or silence alerting) and try to assign to it. In reality lag is derived from f.LastPollSuccess(); to reduce actual lag you must let replication catch up or trigger a sync via the litestream API/CLI.
Related errors
- litestream_txid is read-only
- litestream_hydration_progress is read-only
- could not parse time: %s
- litestream_hydration_file is read-only
- set page size: %w
AI-assisted analysis of benbjohnson/litestream@4ed7a308f6 (2026-09-06).
Data as JSON: /api/errors/f0803b013b707a3d.
Report an issue: GitHub.