{"record":{"id":"9c5d054a23df8538","repo":"benbjohnson/litestream","slug":"litestream-txid-is-read-only","errorCode":null,"errorMessage":"litestream_txid is read-only","messagePattern":"litestream_txid is read-only","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"vfs.go","lineNumber":2439,"sourceCode":"\treturn result.Time.UTC(), nil\n}\n\n// FileControl handles file control operations, specifically PRAGMA commands for time travel.\nfunc (f *VFSFile) FileControl(op int, pragmaName string, pragmaValue *string) (*string, error) {\n\tconst SQLITE_FCNTL_PRAGMA = 14\n\n\tif op != SQLITE_FCNTL_PRAGMA {\n\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","sourceCodeStart":2421,"sourceCodeEnd":2457,"githubUrl":"https://github.com/benbjohnson/litestream/blob/4ed7a308f6271ebfd2b0a6e4b70b03011a37e4a3/vfs.go#L2421-L2457","documentation":"The litestream_txid PRAGMA is read-only: FileControl() in vfs.go returns this error when pragmaValue is non-nil, i.e. the caller attempted `PRAGMA litestream_txid = <value>` to assign to it. Reading (pragmaValue == nil) is valid and returns the current replication position's TXID string.","triggerScenarios":"Executing `PRAGMA litestream_txid = 'xxxx'` (assignment form) on a connection backed by the litestream VFS. In SQLite, the assignment form passes a non-nil pragmaValue into xFileControl, triggering the guard.","commonSituations":"Developers try to 'set' the transaction id, e.g. to force time travel to a specific TXID or restore state, not realizing TXID is an observed property of the replica position, not a writable setting. Time travel is done via PRAGMA litestream_time instead.","solutions":["Remove the assignment; use the read form `PRAGMA litestream_txid;` to query the current TXID","To travel to a point in time, use `PRAGMA litestream_time = '<RFC3339 or relative time or latest>'`","If you need a specific TXID's corresponding time, resolve it from replica LTX files and pass that time to litestream_time","Catch the error and surface a clear message that txid is read-only"],"exampleFix":"// before\nPRAGMA litestream_txid = '00000000-0000-0000-0000-000000000000';\n// after\nPRAGMA litestream_txid;  -- read the current TXID\nPRAGMA litestream_time = '5 minutes ago';  -- travel in time","handlingStrategy":"validation","validationCode":"// Reject assignment form before executing\nif strings.Contains(strings.ToLower(stmt), \"litestream_txid=\") ||\n   strings.Contains(strings.ToLower(stmt), \"litestream_txid =\") {\n    return errors.New(\"litestream_txid is read-only; use the read form PRAGMA litestream_txid;\")\n}","typeGuard":null,"tryCatchPattern":"_, err := db.Exec(\"PRAGMA litestream_txid = ?\", v)\nif err != nil && strings.Contains(err.Error(), \"litestream_txid is read-only\") {\n    // switch to read-only usage\n    row := db.QueryRow(\"PRAGMA litestream_txid\")\n    var txid string\n    _ = row.Scan(&txid)\n}","preventionTips":["Only ever query litestream_txid; never use the assignment form","Use PRAGMA litestream_time for time travel instead of trying to set a TXID","Sanitize/ban write-forms of litestream_* pragmas in ORM/DB helper layers"],"tags":["go","sqlite","pragma","read-only","litestream"],"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"}