{"record":{"id":"6c8f142ae0fa40c0","repo":"tikv/tikv","slug":"txn-record-found-but-not-expected","errorCode":null,"errorMessage":"txn record found but not expected: {:?} {} {:?} {:?} [region_id={}]","messagePattern":"txn record found but not expected: (.+?) (.+?) (.+?) (.+?) \\[region_id=(.+?)\\]","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"src/storage/txn/actions/check_txn_status.rs","lineNumber":314,"sourceCode":"    }\n}\n\npub fn rollback_lock(\n    txn: &mut MvccTxn,\n    reader: &mut SnapshotReader<impl Snapshot>,\n    key: Key,\n    lock: &Lock,\n    is_pessimistic_txn: bool,\n    collapse_rollback: bool,\n) -> Result<Option<ReleasedLock>> {\n    // Lock is never shared in the current branch's architecture - shared locks use\n    // SharedLocks type\n    let overlapped_write = match reader.get_txn_commit_record(&key)? {\n        TxnCommitRecord::None { overlapped_write } => overlapped_write,\n        TxnCommitRecord::SingleRecord { write, commit_ts }\n            if write.write_type != WriteType::Rollback =>\n        {\n            panic!(\n                \"txn record found but not expected: {:?} {} {:?} {:?} [region_id={}]\",\n                write,\n                commit_ts,\n                txn,\n                lock,\n                reader.reader.snapshot_ext().get_region_id().unwrap_or(0)\n            )\n        }\n        _ => return Ok(txn.unlock_key(key, is_pessimistic_txn, TimeStamp::zero())),\n    };\n\n    // If prewrite type is DEL or LOCK or PESSIMISTIC, it is no need to delete\n    // value.\n    if lock.short_value.is_none() && lock.lock_type == LockType::Put {\n        txn.delete_value(key.clone(), lock.ts);\n    }\n\n    // (1) The primary key of any transaction needs to be protected.","sourceCodeStart":296,"sourceCodeEnd":332,"githubUrl":"https://github.com/tikv/tikv/blob/78aedc1c81ef3f7d8bacc6e9d09f56460f134937/src/storage/txn/actions/check_txn_status.rs#L296-L332","documentation":"During a rollback (`rollback_lock`), the code expects that when a transaction commit record exists it is either absent (allowing overlapped writes) or a Rollback. If a WRITE record of any other type (Put/Delete/Lock) is found for the transaction being rolled back, the transaction is actually committed — rolling it back would be incorrect — so TiKV panics with the record, commit_ts, txn, lock, and region id for diagnosis.","triggerScenarios":"`rollback_lock` calls `get_txn_commit_record` and matches `TxnCommitRecord::SingleRecord` whose `write_type != WriteType::Rollback` — i.e. attempting to roll back a lock whose transaction already has a committed Put/Delete/Lock write. Raised from callers like `cleanup`, `check_txn_status_lock_exists`, and `check_txn_status_from_pessimistic_primary_lock`.","commonSituations":"Client/TiDB issuing rollback (cleanup) for a transaction that concurrently committed (race between commit and stale-lock cleanup); stale lock-resolution requests delayed past commit; region leader change with stale reads; primary-lock cleanup after the transaction already committed.","solutions":["Treat this as a serious invariant violation: collect the panic log (write, commit_ts, txn, lock, region_id) and file it with TiKV support.","Verify the transaction's commit status via TiDB/tikv-ctl (`check_txn_status`) before issuing cleanup/rollback for possibly-committed locks.","Check for known bugs in your TiKV version around `get_txn_commit_record` overlap handling and upgrade to a patched release.","Use the region/transaction recovery tooling (unsafe recovery, online unsafe destroy) only with support guidance; do not manually rewrite WRITE CF records."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// before requesting cleanup/rollback, confirm the txn is not committed\nlet status = check_txn_status(txn_key, check_ts).await?;\nif status.lock_info.is_none() && status.commit_ts.is_some() {\n    skip_rollback(\"txn already committed\");\n}","typeGuard":"fn safe_to_rollback(rec: &TxnCommitRecord) -> bool {\n    match rec {\n        TxnCommitRecord::None { .. } => true,\n        TxnCommitRecord::SingleRecord { write, .. } => write.write_type == WriteType::Rollback,\n        _ => false,\n    }\n}","tryCatchPattern":"match result {\n    Err(e) if e.message.contains(\"txn record found but not expected\") => {\n        capture_panic_context(region_id, txn);\n        escalate_to_support(e); // do not retry rollback\n    }\n    other => propagate(other),\n}","preventionTips":["Keep TiDB/TiKV versions aligned to avoid stale lock-resolution races.","Monitor lock-resolve latency; long stale locks increase commit/rollback races.","After region leader changes, let resolution requests use fresh snapshots.","Upgrade to releases with fixes for check_txn_status overlap handling.","Never manually craft cleanup requests for transactions that may have committed."],"tags":["transaction","mvcc","rollback","panic","concurrency"],"backgroundTag":"rollback-of-committed-transaction","analyzedSha":"78aedc1c81ef3f7d8bacc6e9d09f56460f134937","analyzedAt":"2026-09-03T23:31:32.398Z","contentChangedAt":"2026-09-03T23:31:32.398Z","schemaVersion":2},"datasetVersion":"2026-09-11T07:07:21.782Z"}