SQL Server Error 825: read-retry succeeded and the disk is failing
Error 825 is what SQL Server writes to the error log when a disk read failed on the first attempt but succeeded on a retry (attempt 2, 3, or 4). The query completes. The application sees no failure. But the storage underneath just told you it is failing.
Most monitoring setups never surface Error 825. It is a severity-10 informational message, and typical SQL Server Agent alert configurations target severity 19 and above. The error sits quietly in the log until something harder arrives: an 823 (hard I/O error) or an 824 (logical consistency error). By then the page may already be unreadable.
Treat every Error 825 as a page. The read worked this time, but the same sector, controller, or path will eventually return a hard failure. The job now is to identify which file, which volume, and which underlying component is deteriorating before the next read does not recover.
What this means
When SQL Server issues a read, the SQLOS I/O subsystem hands it to the operating system. If the OS returns a failure (bad sector, controller timeout, transport error), SQL Server does not immediately surface it. The engine retries the read up to four times before declaring a hard 823 or 824. If any retry succeeds, the page is returned to the buffer pool, the query continues, and Error 825 is written to the error log.
The message text looks like this:
Error: 825, Severity: 10, State: N.
A read of the file '<path>' at offset <n> succeeded after failing <N> time(s) with error: <OS error text>.
Additional messages in the SQL Server error log and system event log may provide more detail.
This error condition threatens database integrity and must be corrected.
Complete a full database consistency check (DBCC CHECKDB).
Severity 10 is the key gotcha. Microsoft classifies severities 0 through 10 as informational. They do not raise exceptions that flow into TRY…CATCH blocks, and they do not trigger default SQL Server Agent alerts. Without an explicit message_id-based alert on 825, the warning is invisible to operations.
This behavior has not changed since Error 825 was introduced in SQL Server 2005. The same read-retry path, the same severity, and the same alerting gap apply to every currently supported version through SQL Server 2025.
Common causes
| Cause | What it looks like | First thing to check |
|---|---|---|
| Failing disk sector | 825 on one file at one offset; OS event log reports bad blocks | sp_readerrorlog 0, 1, 'Error: 825' and the OS system event log |
| SAN or storage controller degradation | 825 across multiple files on the same LUN; latency spikes on the affected files | Per-file I/O stall deltas during the 825 window |
| HBA, fabric, or cabling fault | 825 entries clustered in time, possibly with link resets in the OS event log | OS event log around the 825 timestamp |
| Storage firmware bug or MPIO path thrashing | 825 spikes during array rebalance, path failover, or firmware updates | Storage array logs; correlate with maintenance windows |
| Cloud disk throttling or transient fault | 825 on cloud VM storage during IOPS or throughput cap bursts | Cloud provider throttle metrics for the disk tier |
| Corrupt page on otherwise healthy media | 825 followed by an 824 on the same page id | msdb.dbo.suspect_pages for that database and page |
Quick checks
Run these in order. All are read-only.
-- 1. Find every 825 in the current error log
EXEC sp_readerrorlog 0, 1, 'Error: 825';
-- 2. Search the most recent archived log if the event rolled over
EXEC sp_readerrorlog 1, 1, 'Error: 825';
-- 3. Check for harder I/O errors in the same window
EXEC sp_readerrorlog 0, 1, 'Error: 823';
EXEC sp_readerrorlog 0, 1, 'Error: 824';
-- 4. Check the suspect_pages table for active corruption records
SELECT database_id, DB_NAME(database_id) AS database_name,
file_id, page_id, event_type, error_count, last_update_date
FROM msdb.dbo.suspect_pages
WHERE event_type IN (1, 2, 3)
ORDER BY last_update_date DESC;
-- 5. Map each database file to its volume and per-file I/O stall
SELECT
DB_NAME(vfs.database_id) AS db,
mf.name AS file_name,
mf.type_desc,
mf.physical_name,
vs.volume_mount_point,
vfs.io_stall_read_ms,
vfs.num_of_reads,
CASE WHEN vfs.num_of_reads > 0
THEN vfs.io_stall_read_ms * 1.0 / vfs.num_of_reads
ELSE 0 END AS avg_read_latency_ms,
vs.available_bytes * 1.0 / vs.total_bytes AS pct_free
FROM sys.dm_io_virtual_file_stats(NULL, NULL) vfs
JOIN sys.master_files mf
ON vfs.database_id = mf.database_id AND vfs.file_id = mf.file_id
CROSS APPLY sys.dm_os_volume_stats(mf.database_id, mf.file_id) vs
ORDER BY vfs.io_stall_read_ms + vfs.io_stall_write_ms DESC;
How to diagnose it
Goal: identify which file, which volume, and which storage component produced the 825, and confirm whether any page is already damaged.
Capture the 825 entries with timestamps and offsets. Run
sp_readerrorlog 0, 1, 'Error: 825'. Each row includes the physical file path, the byte offset, the retry count, and the OS error text. Record the file, the offset, and the time window.Correlate with
sys.dm_io_virtual_file_stats. Snapshot the DMV, wait a measured interval, snapshot again, compute deltas per file. The file named in the 825 should show disproportionateio_stall_read_msgrowth relative to its read count. Compare average read latency against known thresholds: under 10ms excellent, 10-20ms acceptable, over 20ms degraded, over 50ms severe.Check
msdb.dbo.suspect_pages. The table records pages that hit an 823 or 824. Error 825 is a success-after-retry, so a clean retry may not add a row. An emptysuspect_pagesdoes not rule out active storage degradation. Any new row is a page-level alarm on its own. The table retains a maximum of 1000 rows; older entries are purged automatically, so new events may displace uninvestigated old ones.Run
DBCC CHECKDBagainst the database named in the 825. CHECKDB is I/O-intensive and creates an internal database snapshot that consumes TempDB space, so schedule it carefully. CHECKDB can return clean even when 825 is firing, because the retry path may deliver a correct page on a later attempt. A clean CHECKDB does not contradict an 825. Run it anyway: if a page is already torn or checksum-failed, this is where it surfaces.Pull the OS system event log for the same window. Storage subsystems usually log the underlying error before SQL Server does. Look for bad-block events, controller resets, MPIO path failovers, or disk firmware messages.
Check for 825 clustering. A single 825 is a warning. A rising rate, several per hour, then several per minute, is a failing component. Trend the count over hours and days. Increasing frequency toward a hard 823 or 824 is the classic signature of SAN controller failure or deteriorating cabling.
Correlate with backup health. 825 events frequently appear alongside backup failures (Msg 3203, Msg 2013) when the storage cannot deliver a clean read of a data file. If backups started failing around the same time, treat the storage subsystem as the prime suspect.
flowchart TD
A[OS read fails] --> B[SQLOS retries up to 4 times]
B -->|retry ok| C[Error 825 written
query completes]
B -->|all retries fail| D[Error 823 or 824
page unreadable]
C --> E[Invisible to default
SQL Agent alerts]
E --> F[Investigate file, offset,
OS error, suspect_pages]
F --> G{I/O stalls and
suspect_pages correlate?}
G -->|yes| H[Storage degradation
confirmed]
G -->|no| I[Watch frequency
trend]Metrics and signals to monitor
| Signal | Why it matters | Warning sign |
|---|---|---|
| Error 825 in error log | Earliest reliable indicator of failing storage | Any nonzero count in a window |
| Error 823 (hard I/O error) | Read failed all retries; page may be unreadable | Any occurrence is PAGE |
| Error 824 (logical consistency) | Checksum or torn page detected after read | Any occurrence is PAGE |
msdb.dbo.suspect_pages rows | Durable record of pages that hit I/O errors | New row since last check |
Per-file io_stall_read_ms delta | SQL Server’s direct view of storage latency | Sustained average read latency above 20ms |
Per-file io_stall_write_ms delta (log files) | Log write latency directly impacts every commit | Sustained log write latency above 5ms |
PAGEIOLATCH_* wait time | Workers blocked waiting for physical reads | Rising share of total wait time |
| Backup job failures | Storage cannot deliver clean reads for backup | Msg 3203 in job output |
| OS storage event log | Underlying controller, disk, and path errors | Bad-block, path-reset, firmware events |
| Error 825 frequency trend | Predicts escalation to a hard 823 or 824 | Count increasing over consecutive windows |
Fixes
Error 825 is not a SQL Server configuration problem. The engine is reporting a storage problem. The fixes live below SQL Server.
Contain the risk immediately
- Take a full backup before the failing read becomes an unreadable read. A backup taken now is the best recovery point you will have. Verify it with
RESTORE VERIFYONLY. If the backup fails with bad-block errors, you have just confirmed a hard storage fault. - Identify and isolate the affected file and volume. If only one database file is on the deteriorating volume, consider whether you can fail over to a secondary (AlwaysOn) or restore the database to a different volume. Do not wait for the next 823.
- Engage the storage or hypervisor team. Provide them with the file path, offset, OS error text, and timestamp from the 825 entry. They have visibility into array, controller, and disk health that SQL Server does not.
Address the storage component
- Run vendor storage diagnostics against the LUN. Most arrays have a read-only surface scan or SMART-style check that can identify the failing sector without disrupting I/O.
- Replace the failing component. Disk, HBA, cable, or controller. Do not assume a single bad sector is isolated; on enterprise storage, a bad sector often indicates a failing disk surface or a degraded controller cache battery.
- On cloud storage, reattach the disk to a different host or upgrade the disk tier. Throttling-induced 825 errors on cloud VMs indicate you have hit an IOPS or throughput cap for too long. Moving to a higher tier or spreading I/O across additional disks is the structural fix.
Establish a recovery safety net
- Pre-stage a tested restore path. The only way to know your backups are valid is to restore them. Schedule regular restore tests, not just
RESTORE VERIFYONLY. - Maintain a baseline of per-file I/O latency. Without a baseline, you cannot tell whether a 15ms average read is normal for that volume or a recent regression.
- Confirm
PAGE_VERIFY CHECKSUMis enabled on every user database. When the storage does deliver a corrupt page, checksums let SQL Server detect it via Error 824 instead of silently returning wrong data.
Prevention
- Alert on message_id 825 directly. The default SQL Server Agent alert configuration uses severity thresholds that will never fire on a severity-10 message. Add an alert scoped to
@message_id = 825, @severity = 0. The standard reference for this is Brent Ozar’s SQL Server alert script, which includes 825 alongside 823 and 824. - Treat any 825 as a page. Severity 10 means informational in the documentation, but operationally it means the storage just failed a read and got lucky. Page on the first occurrence, not the tenth.
- Keep CHECKDB on a weekly schedule for production databases. Some corruption modes are only detected by CHECKDB. Error 825 may precede them by days or weeks.
- Track
suspect_pagesover time. Investigate every new row. Manually remove resolved entries once investigated so new ones are obvious. The 1000-row cap means old entries are silently displaced. - Snapshot
sys.dm_io_virtual_file_statsexternally. The DMV is cumulative since instance startup. Without periodic external sampling, you have no delta to compare against when 825 fires.
How Netdata helps
- Per-second error log scraping surfaces the first 825 within the same minute it is written, before the next read becomes an unrecoverable 823. Default severity-based alerting misses it; explicit message_id tracking does not.
- Per-file I/O stall correlation (
io_stall_read_msandio_stall_write_msdeltas) lets you confirm that the file named in the 825 is also the file with climbing latency, ruling out random transient reads. PAGEIOLATCH_*wait time trends show whether storage degradation is starting to block workers, which is the path from soft warning to users reporting the database is slow.suspect_pagesrow count monitoring catches the moment a soft 825 escalates to a hard 824 and a row lands inmsdb.- Backup job and duration trending flags the backup failures that often accompany 825 events, before someone needs a restore that does not exist.
Netdata’s Microsoft SQL Server monitoring brings these signals together with per-second metrics and ML anomaly detection.
Related guides
- SQL Server blocking chains: finding the head blocker before workers run out
- SQL Server buffer cache hit ratio low: when the working set no longer fits in memory
- SQL Server user connections climbing: connection pool leaks and retry storms
- SQL Server CPU utilization high: telling query load apart from a bad plan
- SQL Server CXPACKET and CXCONSUMER waits: parallelism, MAXDOP, and what is actually wrong
- SQL Server Error 1205: transaction was deadlocked and chosen as the deadlock victim
- SQL Server Error 701: there is insufficient system memory to run this query
- SQL Server Error 9002: the transaction log for the database is full
- SQL Server high compilations per second: plan cache pollution and CPU burn
- How Microsoft SQL Server actually works in production: a mental model for operators
- SQL Server LCK_M waits high: lock contention and what the suffixes mean
- SQL Server log autogrow stall: why every write pauses while the log file grows






