A restore has stopped, and the log backup you are holding will not apply to it. Which of these three messages you have decides what happens next.
BACKUP LOG ... WITH NORECOVERY before you overwrite anything. None of the three is corruption.All three come from the same rule. A log backup can only be applied if it starts at or before the point the database has reached, and ends after it. 4305 and 4326 are that rule failing in each direction, and SQL Server tells you both numbers it compared. 3159 is the same idea seen from the other end: the live database still holds log records that no backup has captured yet, and it will not let you overwrite them without being told to.
Nothing here needs a new backup or a support call. Every message and every LSN on this page is captured from a throwaway database on SQL Server 2025 (17.0.4075.5): one full backup, three log backups, then applied in the wrong order on purpose.
Read the Two LSNs in the Message
Every 4305 and 4326 carries two log sequence numbers. The first describes the file you tried to apply. The second is where the database currently sits, and the file you need is the one whose range contains it. Get the ranges from msdb:
SELECT bs.backup_set_id,
bs.type, -- D = full, I = differential, L = log
bs.first_lsn,
bs.last_lsn,
bs.database_backup_lsn, -- on a log: the full it chains from
bs.backup_finish_date,
bmf.physical_device_name
FROM msdb.dbo.backupset AS bs
JOIN msdb.dbo.backupmediafamily AS bmf ON bmf.media_set_id = bs.media_set_id
WHERE bs.database_name = N'Sales'
ORDER BY bs.backup_finish_date;
This is the chain that produced the three messages on this page. Each log backup starts exactly where the previous one ended, which is what a healthy chain looks like:
type first_lsn last_lsn database_backup_lsn physical_device_name
D 44000000026400001 44000000028800001 0 ...\Sales_full.bak
L 44000000026400001 44000000033600001 44000000026400001 ...\Sales_log1.trn
L 44000000033600001 44000000044000001 44000000026400001 ...\Sales_log2.trn
L 44000000044000001 44000000047200001 44000000026400001 ...\Sales_log3.trn
Line the message up against it. The 4305 says the database is at 44000000028800001, which is the end of the full backup, and the file that contains that LSN is Sales_log1.trn. The 4326 says the database is at 44000000044000001, the end of log 2, so the next file is log 3 and anything earlier is already in.
If the backups came from another server, msdb on this one knows nothing about them. Ask the files directly instead with RESTORE HEADERONLY FROM DISK = N'D:\Backups\Sales_log2.trn';, and the columns to read are FirstLSN, LastLSN and DatabaseBackupLSN.
And if you have lost track of what has already been applied to the restoring database, the last row in msdb.dbo.restorehistory for it, joined back to backupset, gives the same number the error message does:
SELECT TOP (1) rh.restore_date, rh.restore_type, bs.last_lsn AS database_is_now_at
FROM msdb.dbo.restorehistory AS rh
JOIN msdb.dbo.backupset AS bs ON bs.backup_set_id = rh.backup_set_id
WHERE rh.destination_database_name = N'Sales'
ORDER BY rh.restore_date DESC, rh.restore_history_id DESC;
To check every database on the instance for gaps before an incident rather than during one, Get Backup Chain Integrity runs this reasoning across the whole of msdb.
4305: Too Recent, So a File Is Missing From the Sequence
The file you applied starts after the point the database has reached. Something that covers the gap has not been applied yet. The capture was the simplest version: RESTORE DATABASE Sales FROM DISK = N'D:\Backups\Sales_full.bak' WITH NORECOVERY;, log 1 skipped, then RESTORE LOG Sales FROM DISK = N'D:\Backups\Sales_log2.trn' WITH NORECOVERY;, which raised the message above on Line 3.
The fix is to apply the file whose range contains the second LSN, then carry on in order. The database is untouched by the failure, so nothing has to be restarted:
RESTORE LOG Sales FROM DISK = N'D:\Backups\Sales_log1.trn' WITH NORECOVERY;
RESTORE LOG Sales FROM DISK = N'D:\Backups\Sales_log2.trn' WITH NORECOVERY;
The harder version is when the query against msdb shows a file you do not have. The usual reasons, in the order I would check them:
- A differential was skipped. A log taken after a differential begins where the differential ends. Restore the differential, or every log between the full and that point.
- Something else took a log backup.
physical_device_namein the chain query names it. A second job, a third-party agent, or somebody runningBACKUP LOGto shrink a file all break the sequence for everyone else, and their file is the one you now need. - The file was deleted or never copied. The row is in
msdb, the path is gone.
If the missing file cannot be found anywhere, the chain is broken at that point. You can recover the database to the end of the last file you did apply, with RESTORE DATABASE Sales WITH RECOVERY, and lose everything after it. Or you start again from a full or differential taken after the gap, if one exists. There is no third option, and no amount of retrying changes the arithmetic.
4326: Too Early, So the Database Is Already Past This File
The opposite direction. The file ends before the point the database has reached, so everything in it is already there. In the capture this was log 1 applied after log 2, RESTORE LOG Sales FROM DISK = N'D:\Backups\Sales_log1.trn' WITH NORECOVERY;, and the message above came back on Line 1.
Skip forward. The next file is the one whose first_lsn is at or below the second LSN and whose last_lsn is above it. Again the database is not damaged by the attempt, so continue from where you are.
When you did not apply anything out of order and still get 4326, the base is newer than the logs. That is the trap. A full or differential that was taken after the log backups you are holding has been restored, and every one of those logs is now too early. The same capture shows it: a second full backup taken later, restored as the base, then log 3 from the original chain. That raises the same Msg 4326, Level 16, State 1, this time saying the log terminates at LSN 44000000047200001 and that a more recent log backup that includes LSN 44000000079200001 can be restored.
Check database_backup_lsn on the log against checkpoint_lsn on the full you restored. If they differ, either go back to the older full that the logs chain from, or find the logs taken after the newer one. Restoring a full does not change which logs belong to it.
One thing that is not a 4326: applying the file you just applied a second time. On SQL Server 2025 that was accepted without a message, because the file’s range still contains the current position. Harmless, and worth knowing so you do not read a clean second pass as proof the chain is fine.
3159: Back Up the Tail Before You Overwrite
This one fires on the target, not the file. The database you are restoring over is in FULL or BULK_LOGGED recovery and has log records since its last log backup. Those records are the tail, they exist nowhere else, and the restore will destroy them. SQL Server stops and asks. In the capture it was a plain restore of the full backup over the live database, RESTORE DATABASE Sales FROM DISK = N'D:\Backups\Sales_full.bak' WITH NORECOVERY;, and the message above came back on Line 1.
If the database is online, back the tail up with NORECOVERY. That captures the records and puts the database into RESTORING in one statement, so nothing can write to it between the tail backup and the restore:
BACKUP LOG Sales TO DISK = N'D:\Backups\Sales_tail.trn' WITH NORECOVERY, INIT;
Run the restore again and it goes through. The tail file is the last log you apply, after every scheduled log backup, and the row count at the end matched the live database exactly in testing. If the database is offline or will not start, Microsoft’s guidance is BACKUP LOG Sales TO DISK = N'D:\Backups\Sales_tail.trn' WITH NO_TRUNCATE, INIT; instead, and CONTINUE_AFTER_ERROR on a damaged database, which needs the log file itself to be intact. I did not have a way to damage the data file in the test, so that part rests on the documentation rather than on captured output.
WITH REPLACE is the other answer the message offers, and it is the right one only when you have already decided you do not want the tail. Refreshing a test copy from production, a restore test, a point in time you have chosen deliberately. It does exactly what it says. In the capture the live database had five rows, one of them written after the last log backup: SELECT COUNT(*) FROM Sales.dbo.Orders; returned 5, then RESTORE DATABASE Sales FROM DISK = N'D:\Backups\Sales_full2.bak' WITH REPLACE, RECOVERY; ran, and the same count returned 4.
The fifth row is gone, with no further warning. On a production database that is committed work somebody has already been told succeeded. REPLACE also waives the check behind error 3154, so it will happily overwrite a different database of the same name too. Take the tail backup. It costs seconds, and if it turns out you did not need it, nothing is lost by having it.
The Full Sequence, in Order
Every statement takes NORECOVERY except the last, and recovering is safest as its own statement at the end. Recover early by mistake and the database is usable but closed to further logs, and the only way to apply another file is to start again from the full.
-- 1. Capture the tail, only if the database exists and you want the work in it
BACKUP LOG Sales TO DISK = N'D:\Backups\Sales_tail.trn' WITH NORECOVERY, INIT;
-- 2. The base: the full, then the newest differential if you have one
RESTORE DATABASE Sales FROM DISK = N'D:\Backups\Sales_full.bak' WITH NORECOVERY;
RESTORE DATABASE Sales FROM DISK = N'D:\Backups\Sales_diff.bak' WITH NORECOVERY;
-- 3. Every log taken after that base, in backup_finish_date order
RESTORE LOG Sales FROM DISK = N'D:\Backups\Sales_log3.trn' WITH NORECOVERY;
RESTORE LOG Sales FROM DISK = N'D:\Backups\Sales_log4.trn' WITH NORECOVERY;
-- 4. The tail last, then recover
RESTORE LOG Sales FROM DISK = N'D:\Backups\Sales_tail.trn' WITH NORECOVERY;
RESTORE DATABASE Sales WITH RECOVERY;
A restore that stops part way leaves the database in RESTORING, which is its own message when somebody tries to open it. That is the correct state to be in between steps, not a problem to fix. And for a chain longer than a handful of files, have msdb write the statements rather than typing them: Generate Backup and Restore Scripts emits the whole sequence in order with the right options on each line.
When It Is Not This
| Message | What it means, and the next move |
|---|---|
3154 instead of any of these | A different database, not an ordering problem: error 3154 |
4330, recovery path is inconsistent | A different recovery fork. Compare RecoveryForkID in RESTORE HEADERONLY |
Every log too recent, begins_log_chain = 1 | The chain broke, usually SIMPLE and back. Base after it: what breaks the chain |
3013 on its own | The real error is the line above it: errors 3013 and 3241 |
4335, STOPAT time is too early | Start again from the base, then STOPAT on the log that contains the time |
Microsoft’s reference covers tail-log backups, including the NO_TRUNCATE and CONTINUE_AFTER_ERROR cases, and the ordering rules in Apply Transaction Log Backups in full.
Common Questions
Which file do I restore next?
msdb.dbo.backupset query in Read the Two LSNs and find the row where first_lsn is at or below it and last_lsn is above it. If no row qualifies, the file you need was never recorded here, or was deleted.I only have the .trn files and no msdb history. How do I order them?
RESTORE HEADERONLY against each file returns FirstLSN, LastLSN and DatabaseBackupLSN without restoring anything. Sort by FirstLSN and each file’s LastLSN should equal the next file’s FirstLSN. A gap between two files is the missing backup.Can I skip a log backup?
Is WITH REPLACE the fix for 3159?
BACKUP LOG ... WITH NORECOVERY first, then restore, then apply that tail file last.The tail-log backup fails because the data file is gone. Now what?
BACKUP LOG ... WITH NO_TRUNCATE for an offline database, and CONTINUE_AFTER_ERROR on a damaged one, which can succeed only if the log file itself is intact. If neither works, every transaction since the last log backup is lost and the honest restore ends at that file.I restored the same log twice and it worked. Is the chain wrong?
Does a differential change the order?
Related Scripts
- Get Backup Chain Integrity, finds the gaps in every chain on the instance before a restore finds them for you
- Generate Backup and Restore Scripts, writes the full sequence above from
msdb, in order, with NORECOVERY on every line but the last - Get Last Restore History, what has already been applied to this database, and when
- Differential vs Log Backup, which one restores what, and what breaks the chain
- The Backup Set Holds a Backup of a Different Database (3154), the other check REPLACE waives
- Backup & Recovery scripts, the pillar: coverage, chain checks and restore readiness in one place
Leave a Reply