Fix it now
A patch replaced the binaries and the upgrade scripts then failed, so the instance takes the database offline and will not finish starting. Error 912 names the script and the inner error; 3417 reports that master could not be recovered. Start with trace flag 902, fix what the log names, then remove the flag.
NET START MSSQLSERVER /T902
- Read the newest instance error log first. Error 912 quotes the upgrade script that failed and the inner error number, state and severity, and the messages just before it are the real cause.
- Start with the upgrade scripts skipped. Either add
-T902under Startup Parameters in SQL Server Configuration Manager, or use theNET STARTform above, or runsqlservr.exe -s <instance> -T902from the Binn folder. - Fix what the log named. Microsoft’s documented causes for the common 574 case are conflicting user options, orphaned database users, and jobs owned by logins that no longer exist.
- Remove
-T902from the startup parameters and restart. The scripts run again and should complete. - Confirm with
SELECT SERVERPROPERTY('ProductVersion'), SERVERPROPERTY('ProductUpdateLevel');and check the log shows the upgrade finishing.
Trace flag 902 starts new binaries against system objects that have not been upgraded. Back up master and msdb before using it, keep applications off, and remove it as soon as the blocker is cleared. Microsoft’s instruction is to remove it once the issue is resolved, not to run on it.
If the instance starts clean with no trace flag, you are done. If not, the next section explains what runs after a patch and why it stops.
Why it happens
A cumulative update replaces program files and nothing else. It cannot change the contents of your system databases while the service is stopped, so the first time the newer binaries start, the engine runs the T-SQL upgrade scripts that ship with the update. Those scripts live in the MSSQL\Install folder under the instance directory, which is why the script name in the error message looks like a filename: it is one.
The engine treats the outcome as all or nothing, because half-upgraded system objects are worse than none. Error 912’s published text says so plainly: the database will be taken offline, and if the failure was in master it will prevent the entire instance from starting. It also tells you to examine the previous error log entries, take corrective action, and restart so the scripts run to completion. Error 3417 sits alongside it and reports the failure to recover master. Setup, meanwhile, reports “Wait on Database Engine recovery handle failed” and tells you nothing useful.
Trace flag 902 skips script upgrade mode so the instance starts anyway. That gives you a connection to fix the blocker. It does not run the scripts, so the instance is in an unsupported half-patched state for as long as the flag is set.
Error 574 is the most common companion, and its cause is more specific than it looks. Microsoft’s account is that the upgrade scripts run inside a transaction, and where system objects or permissions have been modified a script can fail and leave an orphaned open transaction behind. A later script then calls sp_configure, which is a CONFIG statement, and a CONFIG statement cannot be used inside a user transaction. So 574 is a symptom of an earlier failure, not the first thing that went wrong.
574 in the log: an orphaned transaction from an earlier script
You have this one if The log shows 574, then 912 naming a script such as sqlagent100_msdb_upgrade.sql, and often 3417 after that.
- Start with
-T902set, then read the log for whatever failed before the 574. That is the transaction that never closed. - Clear conflicting user options, which is Microsoft’s first named remedy:
EXEC sp_configure 'user options', '0';thenRECONFIGURE WITH OVERRIDE; - Find orphaned database users by joining
sys.database_principalstosys.server_principalson SID and keeping the rows with no server principal. - Find jobs whose owner no longer exists by joining
msdb.dbo.sysjobs_viewtomaster.dbo.syslogins, and reassign them.
IMPLICIT_TRANSACTIONS set through user options is the classic case. It turns ordinary statements inside the upgrade scripts into open transactions, which is exactly the state a CONFIG statement refuses to run in.
A system database log is full or its drive has no space
You have this one if A log-full or allocation error sits next to the script failure, or the volume holding the system database files is out of room.
- Start with
-T902set, then free space on the volume or grow the log for the database named. - If the database is in full recovery, back up its log so the space becomes reusable.
- Remove the trace flag and restart, watching the log as the scripts run.
msdb is owned by the wrong account
You have this one if 33009 in the error log, typically after msdb was restored from another server or the sa account was renamed.
- With
-T902set, check ownership:SELECT name, SUSER_SNAME(owner_sid) FROM sys.databases; - Correct it:
ALTER AUTHORIZATION ON DATABASE::msdb TO sa;using whatever the sa account is named on this instance. - Remove the trace flag and restart.
The published text of 33009 is that the owner SID recorded in master differs from the one recorded in the database, and it names ALTER AUTHORIZATION as the fix. The same mismatch causes trouble in Database Mail and Agent job ownership, so it is worth fixing even after the patch goes through.
A system database is suspect or read-only
You have this one if 926 for msdb or model, or an attach failure, appears before the script error.
- With
-T902set, checkSELECT name, state_desc, is_read_only FROM sys.databases; - Restore msdb from a recent backup if it is suspect. That is the supported route and it keeps your jobs, operators and history.
- Only if there is no backup, rebuild the system databases from the setup media, then restore what you can.
Rebuilding system databases discards everything held in master and msdb: logins, jobs, linked servers, credentials and history. It is the last option, not the first.
Full reference
Three documented ways to set trace flag 902
| Method | How |
|---|---|
| Configuration Manager | SQL Server Services, right-click the instance, Properties, Startup Parameters, type -T902, Add, OK, then start the service |
sqlservr.exe |
From the instance’s Binn folder: sqlservr.exe -s MSSQLSERVER -T902, or -s <instance name> for a named instance. Stop it with Ctrl+C then Y |
NET START |
NET START MSSQLSERVER /T902, or NET START MSSQL$INSTANCENAME /T902 |
Whichever route you use, remove it the same way once the blocker is cleared. A -T902 left in the startup parameters means the upgrade scripts never run and the instance stays half patched indefinitely.
Reading the failure
| What appears in the error log | What it means |
|---|---|
| 912 naming a script file and an inner error number | That inner error is the real problem. Work on it, not on 912 |
| 3417 | master could not be recovered. It accompanies 912 rather than adding information |
| 574 | A CONFIG statement ran inside a user transaction. Look further back for what opened it |
| 33009 | The database owner SID in master differs from the one in the database |
| 926 | The database is marked SUSPECT by recovery and cannot be opened |
| An allocation or log-full error | Space, on the volume or in the log file, for the database named |
Finding the two orphan cases Microsoft names
SELECT dp.type_desc, dp.SID, dp.name AS user_name
FROM sys.database_principals AS dp
LEFT JOIN sys.server_principals AS sp ON dp.SID = sp.SID
WHERE sp.SID IS NULL AND authentication_type_desc = 'INSTANCE';
SELECT sj.name AS Job_Name, sl.name AS Job_Owner
FROM msdb.dbo.sysjobs_view AS sj
LEFT JOIN master.dbo.syslogins AS sl ON sj.owner_sid = sl.sid
WHERE sl.name <> 'sa'
ORDER BY sj.name;
Commands worth having open
| Statement or place | What it gives you |
|---|---|
| Startup Parameters in Configuration Manager | Where -T902 is added and, more importantly, removed |
SELECT SERVERPROPERTY('ProductVersion'), SERVERPROPERTY('ProductUpdateLevel'); |
The build and update level actually running |
SELECT name, state_desc, is_read_only, SUSER_SNAME(owner_sid) FROM sys.databases; |
State and ownership of every system database in one look |
ALTER AUTHORIZATION ON DATABASE::msdb TO sa; |
Corrects an msdb owner mismatch |
EXEC sp_configure 'user options'; |
Whether something like IMPLICIT_TRANSACTIONS is set instance-wide |
<instance directory>\MSSQL\Install |
The upgrade scripts themselves, named in the 912 message |
Other inner errors Microsoft documents for this failure
Microsoft maintains a set of articles for specific upgrade-script failures, each keyed on the inner error rather than on 912. Among them are 574 for the CONFIG statement case, 945 where SSISDB is part of an availability group, 1712 for online index operations on a lower edition, 2714 for an object that already exists, 4860 for a missing file, 5133 where the temporary database could not be created, 15151 and 15173 for principal problems, and 17182 where TLS 1.0 has been disabled. If the inner error in your log is one of those, search for it directly rather than reading general 912 advice.
What not to do
- Do not uninstall the update. Removing it will not undo scripts that already ran, and it leaves you unpatched with the original problem intact.
- Do not leave -T902 set and carry on. The instance is running new binaries against un-upgraded system objects, which is not a supported state.
- Do not restore master from a different server to ‘fix’ ownership. Correct the owner with ALTER AUTHORIZATION instead.
- Do not skip the backups. You are about to work on master and msdb, so back them up before you change anything.
When a licence is the actual fix
Nothing in this article costs money. Trace flag 902, the log reading and every remedy above are free, and the fix is almost always a system object somebody changed years ago. A licence becomes the answer only in one situation: the build you are patching no longer receives cumulative updates, so there is no update to apply and no supported way forward on it. Moving to a serviced release is a purchase rather than a repair, typically SQL Server 2025 Standard (2-core pack) for an ordinary production instance, licensed against the cores that instance runs on. Arco can check whether your build is still serviced before you spend anything, and size the core count if it is not.
Every code this article covers
| Code | What it points at | Source |
|---|---|---|
912 |
A script level upgrade for a system database failed at a named step, so the database is taken offline; if it was master, the instance will not start | Microsoft Learn |
574 |
A CONFIG statement was run inside a user transaction, typically an sp_configure call caught in a transaction an earlier failed upgrade step left open | Microsoft Learn |
33009 |
The database owner SID recorded in master differs from the one recorded in the database itself | Microsoft Learn |
926 |
The database cannot be opened because recovery marked it SUSPECT; the error log carries the reason | Microsoft Learn |
Confirm the fix worked
- The service starts with no trace flag in the startup parameters and stays running.
- The error log for that clean start shows the script upgrade completing, followed by the ready for connections entry, and no 912 or 3417.
SERVERPROPERTY('ProductVersion')matches the build you installed andProductUpdateLevelshows the update.SELECT name, state_desc FROM sys.databases;shows every system database ONLINE.- SQL Server Agent starts and a test job runs to completion.
Questions people ask about this
Can I just uninstall the update?
Removing an update is possible but it will not undo scripts that already ran, and it leaves you unpatched with the original blocker still in place. Clearing the blocker and letting the scripts finish is both faster and supported.
Are my user databases at risk?
Upgrade scripts change system objects in master and msdb, not your data, and your databases were not opened during the failed startup. Back them up anyway, because you are about to work on master and msdb.
How long can I safely run with -T902 set?
Microsoft’s instruction is to remove it once the underlying issue is resolved, so that setup can restart the upgrade script phase. It is a door to get in through, not a running configuration, and some behaviour will be odd while it is set.
Why does setup say ‘Wait on Database Engine recovery handle failed’ instead of anything useful?
Because that is all setup can see. The scripts run inside the engine after the binaries are replaced, so when they fail setup only knows the instance did not come up. Errors 912 and 3417 in the instance error log are the real report.
Do I need to buy anything?
Not for the fixes here, which are all free. The only licensing question arises if the build you are on is no longer serviced, in which case moving to a supported version is a purchase rather than a repair.
