Read the first line again. It succeeded. That is why this error is dangerous.
Severity 10 is informational, so it raises no alert, fails no job, and returns nothing to the application. Everything carries on working. Meanwhile SQL Server has just told you that your storage returned bad data and only got it right on a retry.
825 is the polite warning before 823 and 824, which are the ones that take a database offline and end with you reaching for backups. On most systems it is the only advance notice you get, and on most systems nobody is listening.
What It Actually Means
SQL Server asked the storage layer for a page. The read came back wrong, either failing outright or returning data whose checksum did not match. SQL Server retried, up to four times, and one of the retries worked.
The data is fine right now. The hardware or the path to it is not.
This is not a SQL Server problem and no amount of DBCC will fix it. Something between the buffer pool and the platter is intermittently wrong: a failing disk, a controller, a driver, a cable, a flaky HBA, a SAN path, or a hypervisor storage layer.
Find Out Whether It Has Happened Before
EXEC xp_readerrorlog 0, 1, N'succeeded after failing', NULL, NULL, NULL, N'DESC';
Run it against the older logs as well, because this is exactly the message that has been quietly recurring for weeks:
DECLARE @i INT = 0;
WHILE @i < 6
BEGIN
BEGIN TRY
EXEC xp_readerrorlog @i, 1, N'succeeded after failing';
END TRY
BEGIN CATCH
END CATCH
SET @i += 1;
END
A single occurrence is worth investigating. A pattern is a failing device, and the interval between occurrences shortening is the thing to watch for.
Check whether the same window shows anything worse:
EXEC xp_readerrorlog 0, 1, N'823';
EXEC xp_readerrorlog 0, 1, N'824';
Check the Suspect Pages Table
SQL Server records pages it had trouble with, and this table is almost never looked at:
SELECT DB_NAME(database_id) AS database_name,
file_id,
page_id,
event_type, -- 1 = 823/824 error, 2 = bad checksum, 3 = torn page,
-- 4 = restored, 5 = repaired, 7 = deallocated
error_count,
last_update_date
FROM msdb.dbo.suspect_pages
ORDER BY last_update_date DESC;
Any row here deserves attention. An empty table is good news; rows with event_type 2 or 3 mean real corruption was seen, whether or not a retry rescued it.
What To Do
In this order, because the order matters:
- 1. Check your backups are good and restorable. Not that they exist, that they restore. Do this before anything else, because everything after this can make things worse if it turns out you need them.
- 2. Run
DBCC CHECKDBwithPHYSICAL_ONLYfirst, which is much faster and catches exactly this class of problem:
Then a fullDBCC CHECKDB (YourDb) WITH PHYSICAL_ONLY, NO_INFOMSGS;DBCC CHECKDBwhen you have a window, if the quick one comes back clean. - 3. Take it to whoever owns the storage with the offsets and timestamps from the error log. This is a hardware conversation and the log entries are the evidence.
- 4. Check the Windows system event log in the same window. Disk, storage driver and HBA errors there confirm it and give the storage team something to act on.
Do not run REPAIR_ALLOW_DATA_LOSS because of an 825. Nothing is broken yet. That command is for corruption you have confirmed and cannot restore around, and it does exactly what it says.
Stopping It Being Missed
- Alert on 825 explicitly. It is severity 10, so no default alert catches it. This is the single most valuable thing in this post: an Agent alert on message 825 turns a silent warning into a phone call while there is still time.
- Alert on 823 and 824 as well, which most people do, and check
suspect_pageson a schedule. - Keep
PAGE_VERIFY CHECKSUMon. Without it SQL Server has no way to notice a bad read at all:
Anything other thanSELECT name, page_verify_option_desc FROM sys.databases;CHECKSUMmeans these errors cannot be detected on that database. - Run
DBCC CHECKDBregularly. Not because it prevents corruption, but because it dates it. A clean check last week tells you how far back you can safely restore.
Common Questions
It says it succeeded, so is there a problem?
Why did nothing alert me?
Should I run REPAIR_ALLOW_DATA_LOSS?
Related Scripts
- Get Suspect Pages and Integrity Checks, the table this post tells you to read, across every database
- Get Last DBCC CHECKDB, when each database was last verified clean
- DBCC CHECKDB Found Corruption, what to do when the quick check does not come back clean
- Get Backup Chain Integrity, find the gaps before you need them
- Get Error Log Patterns, catch repeating messages like this before they escalate
- Get Backup Coverage, which databases have no recent backup at all
Leave a Reply