{"record":{"id":"778b6a2dea43e37d","repo":"rqlite/rqlite","slug":"erropentransaction","errorCode":"ErrOpenTransaction","errorMessage":"open transaction at end of WAL file","messagePattern":"open transaction at end of WAL file","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"db/wal/compacting_section_scanner.go","lineNumber":17,"sourceCode":"package wal\n\nimport (\n\t\"encoding/binary\"\n\t\"errors\"\n\t\"expvar\"\n\t\"fmt\"\n\t\"io\"\n\t\"maps\"\n\t\"math\"\n\t\"sort\"\n\t\"time\"\n)\n\nvar (\n\t// ErrOpenTransaction is returned when the final frame in the WAL file is not a committing frame.\n\tErrOpenTransaction = errors.New(\"open transaction at end of WAL file\")\n)\n\n// CompactingFrameScanner implements WALIterator to iterate over compacted WAL\n// frames starting from a given frame index. It scans all valid frames from the\n// start position to the end of the WAL (or the first invalid frame), keeps only\n// the latest version of each page (respecting transaction boundaries), and\n// returns them in file offset order. If fullScan is false, frame checksums are\n// not verified since the WAL file is trusted.\ntype CompactingFrameScanner struct {\n\treadSeeker io.ReadSeeker\n\twalReader  *Reader\n\theader     *WALHeader\n\tfullScan   bool\n\tstart      int64\n\n\t// pageBuf is a scratch buffer reused across Next() calls to avoid a\n\t// page-sized allocation per frame. The Frame returned by Next aliases\n\t// this buffer in its Data field; see Next's doc comment.","sourceCodeStart":1,"sourceCodeEnd":35,"githubUrl":"https://github.com/rqlite/rqlite/blob/7586a4d1bdbd9a5a80021664c5a863cd850adb60/db/wal/compacting_section_scanner.go#L1-L35","documentation":"ErrOpenTransaction is returned by the CompactingFrameScanner when the final frame in a WAL file is not a commit frame, i.e. the WAL ends mid-transaction. SQLite treats trailing non-committed frames as incomplete, so the scanner refuses to process them rather than silently dropping or compacting a partial transaction.","triggerScenarios":"Passing a WAL file whose last frame lacks the commit flag to NewCompactingFrameScanner / scan — e.g. a WAL truncated at db/wal/compacting_section_scanner.go:250 because the writer crashed or the file was copied mid-write.","commonSituations":"Copying or snapshotting a live SQLite database without proper locking; process crash mid-transaction; truncated WAL transfer between nodes; test fixtures that cut the WAL at a frame boundary.","solutions":["Discard or regenerate the incomplete WAL — SQLite will ignore non-commit frames on recovery","Re-copy the database and WAL with the SQLite backup API or the checkpoint API instead of raw file copy","Ensure the WAL file transfer completes atomically before scanning"],"exampleFix":"// before\nscanner, err := NewCompactingFrameScanner(bytes.NewReader(truncated), 0, false)\n// after\nscanner, err := NewCompactingFrameScanner(bytes.NewReader(fullWAL), 0, false)\nif errors.Is(err, dbwal.ErrOpenTransaction) {\n    log.Printf(\"WAL ends mid-transaction; ignoring trailing frames\")\n}","handlingStrategy":"try-catch","validationCode":"// inspect final frame commit flag before scanning\n// a frame whose header lacks the commit bit means the WAL ends mid-transaction","typeGuard":"func walEndsInCommit(wal []byte) bool {\n    if len(wal) < 32+24 { return false }\n    off := len(wal) - 24\n    return binary.BigEndian.Uint32(wal[off+4:off+8]) != 0 // commit field != 0\n}","tryCatchPattern":"iter, err := NewCompactingFrameScanner(r, start, check)\nif errors.Is(err, ErrOpenTransaction) {\n    // treat trailing frames as incomplete; fall back to full WAL or regenerate\n    return handleIncompleteWAL(err)\n}","preventionTips":["Copy WAL files atomically (SQLite backup API or checkpoint first)","Never truncate a WAL at an arbitrary frame boundary","Validate the last frame is a commit frame before compacting","Treat ErrOpenTransaction as 'incomplete tail', not fatal data loss"],"tags":["sqlite","wal","corruption","transactions"],"backgroundTag":"wal-truncated-mid-transaction","analyzedSha":"7586a4d1bdbd9a5a80021664c5a863cd850adb60","analyzedAt":"2026-09-03T07:03:02.260Z","contentChangedAt":"2026-09-03T07:03:02.260Z","schemaVersion":2},"datasetVersion":"2026-09-10T12:17:11.382Z"}