{"record":{"id":"f0803b013b707a3d","repo":"benbjohnson/litestream","slug":"litestream-lag-is-read-only","errorCode":null,"errorMessage":"litestream_lag is read-only","messagePattern":"litestream_lag is read-only","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"vfs.go","lineNumber":2447,"sourceCode":"\t\treturn nil, fmt.Errorf(\"unsupported file control op: %d\", op)\n\t}\n\n\tname := strings.ToLower(pragmaName)\n\n\tf.logger.Debug(\"file control\", \"pragma\", name, \"value\", pragmaValue)\n\n\tswitch name {\n\tcase \"litestream_txid\":\n\t\tif pragmaValue != nil {\n\t\t\treturn nil, fmt.Errorf(\"litestream_txid is read-only\")\n\t\t}\n\t\ttxid := f.Pos().TXID\n\t\tresult := txid.String()\n\t\treturn &result, nil\n\n\tcase \"litestream_lag\":\n\t\tif pragmaValue != nil {\n\t\t\treturn nil, fmt.Errorf(\"litestream_lag is read-only\")\n\t\t}\n\t\tlastPoll := f.LastPollSuccess()\n\t\tif lastPoll.IsZero() {\n\t\t\tresult := \"-1\" // Never polled successfully\n\t\t\treturn &result, nil\n\t\t}\n\t\tlag := int64(time.Since(lastPoll).Seconds())\n\t\tresult := strconv.FormatInt(lag, 10)\n\t\treturn &result, nil\n\n\tcase \"litestream_time\":\n\t\tif pragmaValue == nil {\n\t\t\tresult := f.currentTimeString()\n\t\t\treturn &result, nil\n\t\t}\n\n\t\tif strings.EqualFold(*pragmaValue, \"latest\") {\n\t\t\tif err := f.ResetTime(context.Background()); err != nil {","sourceCodeStart":2429,"sourceCodeEnd":2465,"githubUrl":"https://github.com/benbjohnson/litestream/blob/4ed7a308f6271ebfd2b0a6e4b70b03011a37e4a3/vfs.go#L2429-L2465","documentation":"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).","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nPRAGMA litestream_lag = 0;  -- attempt to reset lag\n// after\nPRAGMA litestream_lag;  -- read current lag in seconds (-1 = never polled)\n// to reduce lag, trigger replication:\n//   litestream replicate ... / use SyncAndWait() from the Go API","handlingStrategy":"validation","validationCode":"// Reject assignment form before executing\nif strings.Contains(strings.ToLower(stmt), \"litestream_lag=\") ||\n   strings.Contains(strings.ToLower(stmt), \"litestream_lag =\") {\n    return errors.New(\"litestream_lag is read-only; use the read form PRAGMA litestream_lag;\")\n}","typeGuard":null,"tryCatchPattern":"_, err := db.Exec(\"PRAGMA litestream_lag = ?\", v)\nif err != nil && strings.Contains(err.Error(), \"litestream_lag is read-only\") {\n    // fall back to reading current lag\n    row := db.QueryRow(\"PRAGMA litestream_lag\")\n    var lag string\n    _ = row.Scan(&lag)\n}","preventionTips":["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"],"tags":["go","sqlite","pragma","read-only","replication-lag"],"backgroundTag":"read-only-pragma-assignment","analyzedSha":"4ed7a308f6271ebfd2b0a6e4b70b03011a37e4a3","analyzedAt":"2026-09-06T18:29:25.564Z","contentChangedAt":"2026-09-06T18:29:25.564Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}