This usually turns up at the worst possible moment, when a production backup has been copied onto an older development or DR box and the restore is the last step of something that was supposed to take twenty minutes. The backup file is fine. The server will not open it.
SQL Server upgrades a database the first time a newer engine opens it, and that upgrade is one-way. Nothing writes the old format back. So a database that has ever been on a newer version cannot be restored, attached or made to work on an older one, and the sooner you accept that the sooner you pick one of the routes further down.
Read the Two Numbers in the Message
Everything you need to decide what to do next is already in the error. The first number is the internal database version stamped into the backup, the second is the highest internal version this instance can open. You do not have to look either of them up to know you are stuck, you only need them to work out how far apart the two servers are.
You can read both for yourself. The backup header still opens even though the restore will not, which is the part that misleads people:
-- Works against a backup your instance cannot restore.
RESTORE HEADERONLY FROM DISK = N'D:\Backup\Sales.bak';
-- What this instance stamps on its own databases.
SELECT DATABASEPROPERTYEX('master', 'Version') AS db_version,
CONVERT(varchar(30), SERVERPROPERTY('ProductVersion')) AS build;
The header is about sixty columns wide and six of them answer the question. On a SQL Server 2022 instance reading a backup made on SQL Server 2025, that run returned DatabaseVersion 998, CompatibilityLevel 170, SoftwareVersionMajor 17, SoftwareVersionMinor 0, SoftwareVersionBuild 4075 and BackupTypeDescription Database.
SoftwareVersionMajor is the one to read first, because it maps straight onto the product name. A 17 is SQL Server 2025, a 16 is SQL Server 2022, and the gap between that and the major version of your target tells you how big the job is. DatabaseVersion is the internal stamp the engine actually checks, and it is the number the error quotes. On the two instances behind this post, the 2025 instance stamps 998 and the 2022 instance stamps 957 while reporting that it supports up to 958. SQL Server Builds: Complete Version List and Support Lifecycle is the lookup for turning a build number into a product and a support date.
What Still Works and What Does Not
This catches people out, so it is worth stating plainly. Against the same backup file, on the same older instance, two of the three read-only RESTORE commands succeed:
RESTORE HEADERONLYsucceeds. It reads the backup header, which has not changed format.RESTORE FILELISTONLYsucceeds. Logical file names and sizes, which is why people assume the restore is oneWITH MOVEaway.RESTORE VERIFYONLYfails with 3169, because verifying means checking it could be restored here, and it could not.RESTORE DATABASEfails with 3169, followed by 3013.
The actual restore attempt, captured on the 2022 instance:
Msg 3169, Level 16, State 1, Line 1
The database was backed up on a server running database version 998. That version is incompatible with this server, which supports version 958. Either restore the database on a server that supports the backup, or use a backup that is compatible with this server.
Msg 3013, Level 16, State 1, Line 1
RESTORE DATABASE is terminating abnormally.
That 3013 is the generic “the restore stopped” message and it carries no information of its own. If you arrived here from 3013 and the line above it is not 3169, RESTORE Is Terminating Abnormally (Errors 3013 and 3241) covers the other causes. If the complaint is about a different database rather than a version, that is The Backup Set Holds a Backup of a Database Other Than the Existing Database (Error 3154).
Attaching the Data Files Fails the Same Way (Error 948)
The next idea is always the same: skip the backup, copy the .mdf and .ldf across and attach them. It fails for the same reason with a different number, and the wording is blunter:
With the data files copied from a SQL Server 2025 instance to a SQL Server 2022 one, CREATE DATABASE ... FOR ATTACH returns Msg 1813, Level 16, State 2, “Could not open new database ‘Lab1001_att’. CREATE DATABASE is aborted.”, and then the 948 above.
Severity 20 and the phrase “a downgrade path is not supported” are the engine being as clear as it ever gets. Note also that the file numbers are the same 998 and 958 as the backup, because the stamp lives in the data file and the backup simply carries a copy of it. Detach and attach is documented in Database detach and attach, and MS Docs has the error itself as MSSQLSERVER_948.
Setting the Compatibility Level Does Not Help
This is the single most common wasted hour on this problem, so it gets its own section. Compatibility level and internal database version are different things that both look like version numbers.
- Compatibility level (110, 120, 130 up to 170) controls query optimiser and language behaviour. It is a setting you change at any time with
ALTER DATABASE, and in the backup header it shows asCompatibilityLevel 170. - Internal database version (the 998 and 958 in the error) is the physical on-disk format. It is not a setting. The engine raises it when it opens a database for the first time and there is no command that lowers it.
Setting a database on the newer server to compatibility level 160 before you back it up changes the second number and not the first, so the backup still carries 998 and the restore still fails with 3169. There is no point taking a fresh backup after changing it.
The Routes That Actually Work
There are only two shapes of answer here. Either the target stops being older, or you stop moving a database and start moving its contents.
1. Upgrade or rebuild the target. This is the right answer far more often than people expect, because the target is usually a development or DR box that is behind by accident rather than by policy. An in-place upgrade or a side-by-side build both work, and SQL Server Version Upgrade Runbook is the sequence. Run Get Migration Risk Assessment against the source first so that the upgrade is a decision rather than a surprise. MS Docs lists what each version can be upgraded from in Supported version and edition upgrades.
2. Script the schema and move the data. When the target genuinely has to stay where it is, you rebuild the database rather than restore it. Three tools, in order of how much you are moving:
- Generate Scripts in SSMS, with Advanced set to script schema and data, and the target server version set to the older one so that it refuses to emit syntax the target cannot parse. Fine up to a few hundred megabytes, painful beyond that. MS Docs: Generate and publish scripts.
- A dacpac or bacpac. A dacpac carries schema, a bacpac carries schema and data, and both are version-aware, so a dacpac built against the older target will tell you up front which objects it cannot create. This is the route to prefer when the schema is large and the data is not. MS Docs: Data-tier applications.
- Schema by script, data by bcp. For anything large this is the one that finishes. Create the schema on the target without indexes or foreign keys, bulk copy the tables out and in, then add the constraints and indexes. MS Docs: bcp utility.
Whichever you pick, the thing people forget is everything that is not in the database. Logins, SQL Server Agent jobs, linked servers, certificates and server-level settings do not travel in a script of one database, and on a restore they would not have travelled either. SQL Server Migration Runbook: Standalone Instance has the list.
What is not a route: there is no WITH option on RESTORE that downgrades, no trace flag, no supported tool from Microsoft that converts a data file to an older format, and no cumulative update that adds one. The restriction is stated in Restore and recovery overview and the command reference is RESTORE statements.
How Not to Be Here Again
Two habits remove this from your year entirely.
Upgrade the lower environments first. Development and DR boxes being behind production is the direct cause of every instance of this error. Upgrading production first and leaving them is the ordering that breaks the restore test, and the restore test is the thing you most need to keep working.
Record the version alongside every backup. If your restore script reads the header before it restores, it can say “this backup is from a newer version” in plain words instead of surfacing 3169 at 2am. Generate Restore With Move Script and How to Restore a Database in SQL Server are where that check belongs.
Common Questions
Can I restore a SQL Server 2022 backup to SQL Server 2019?
What if I set the compatibility level down before taking the backup?
RESTORE HEADERONLY worked, so why will the restore not?
Can I copy the mdf and ldf and attach them instead?
Is there a tool that downgrades a database?
How big a job is the script and copy route really?
The restore failures that look like this one but are not, and the migration that replaces it.
- RESTORE Is Terminating Abnormally (3013 and 3241), when the line above 3013 is not a version complaint.
- The Backup Set Holds a Backup of a Different Database (3154), the other restore refusal people meet first.
- SQL Server Version Upgrade Runbook, upgrading the target so the restore just works.
- SQL Server Migration Runbook: Standalone Instance, everything outside the database that has to move with it.
- DBA Scripts: Backups and Recovery, the pillar this sits in.
Leave a Reply