Errors 823 and 824 are SQL Server’s severity-24 storage integrity alarms. 823 means the operating system reported a hard failure on a file API call. 824 means the call succeeded but the page failed an internal integrity check. Both are PAGE-worthy the moment they appear; they do not self-resolve, and continued use of the affected files risks losing data that was fine minutes earlier.

Error 825 is the soft warning that usually precedes both: SQL Server retried a read that initially failed and eventually succeeded. The query did not fail and no connection was killed, which is why most teams do not alert on it. It is also the most reliable predictor that an 823 or 824 is coming.

The first response is always the same: confirm the corruption with DBCC CHECKDB, check the storage layer, and restore from a known-good backup if corruption is confirmed. Do not reach for DBCC CHECKDB with REPAIR_ALLOW_DATA_LOSS as a first move. Microsoft’s documentation is explicit that this option can result in more data loss than restoring from your last known-good backup.

What this means

Error 823 (MSSQLSERVER_823), severity 24. SQL Server uses Windows file APIs (ReadFile, WriteFile, ReadFileScatter, WriteFileGather) for database file I/O. When one of these calls fails with an OS error code, SQL Server reports 823. For reads, SQL Server retries the request four times before surfacing 823 to the caller; writes are reported immediately. The message includes the database file path, the physical byte offset, whether the operation was a read or a write, and the OS error code. The query that touched the page is killed; the user sees a severity-24 error.

Error 824 (MSSQLSERVER_824), severity 24. The OS reported the I/O as successful, but SQL Server found something wrong with the page it received. Possible failures include a checksum mismatch (PAGE_VERIFY CHECKSUM), a torn page (PAGE_VERIFY TORN_PAGE_DETECTION), an incorrect page ID, a stale read, a short transfer, or a page audit failure. The query is killed. The 824 message names the failed check and the file offset.

Error 825 (MSSQLSERVER_825). A read of file %ls at offset %#016I64x succeeded after failing N times with error %ls. The connection is not killed, but the storage stack underneath that file is unreliable. Treat every 825 as the canary before an 823 or 824.

msdb.dbo.suspect_pages. Every 823 and most 824 events write a row to msdb.dbo.suspect_pages in the msdb system database. This table persists across instance restarts, unlike the recycled error log, and is the durable forensic record. The event_type column encodes what happened: 1 = 823 or 824 other than a bad checksum or torn page, 2 = bad checksum, 3 = torn page, 4 = restored (after a page restore), 5 = repaired (after a DBCC repair), 7 = deallocated. The table is capped at 1,000 rows and must be pruned manually; once full, new events are silently dropped.

flowchart TD
    A[SQL Server I/O request] --> B{Windows file API call}
    B -- fails, retried up to 4x --> C{Retry succeeds?}
    C -- Yes --> D[Error 825 warning]
    C -- No --> E[Error 823]
    B -- succeeds --> F{Page integrity check}
    F -- OK --> G[Page served]
    F -- bad checksum, torn page, bad page ID --> H[Error 824]
    E --> I[suspect_pages row, PAGE alert]
    H --> I
    I --> K[Run DBCC CHECKDB, then restore or repair]

Common causes

CauseWhat it looks likeFirst thing to check
Storage hardware failure823 with a hardware-related OS error code; 825s clustering on the same fileArray health, HBA log, smartctl, Windows disk events
Torn page from crash or power loss824 “torn page” on data file pages modified just before an unclean shutdownLast clean shutdown vs last committed log block
Stale read from VM snapshot824 “stale read” or random checksum failures after hypervisor snapshot revert or consolidationHypervisor snapshot tree and consolidation state
Filter driver in the I/O pathIntermittent 823 / 824 with AV, backup VSS, or encryption minifilters activefltmc filters and fltmc instances
Failing controller, cable, backplane825 escalating to 823 on one LUN onlyOther LUNs on the same path, controller firmware events
PAGE_VERIFY not CHECKSUM824 with “invalid protection option” state, or undetected corruption surfaced much latersys.databases.page_verify_option_desc
Storage I/O timeout under load823 during backup, index rebuild, or large scansI/O latency per file via sys.dm_io_virtual_file_stats

Quick checks

Run these read-only checks before changing anything. The goal is to confirm which file, which page, and how widespread.

-- 1. Recent 823/824/825 entries in the current error log
EXEC sp_readerrorlog 0, 1, 'Error: 823';
EXEC sp_readerrorlog 0, 1, 'Error: 824';
EXEC sp_readerrorlog 0, 1, 'Error: 825';
-- 2. Durable record of every I/O consistency event (survives restart)
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;
-- 3. Database state and PAGE_VERIFY option
SELECT name, state_desc, page_verify_option_desc, log_reuse_wait_desc
FROM sys.databases
ORDER BY name;
-- 4. Per-file I/O latency to localize the failing disk
SELECT
    DB_NAME(vfs.database_id) AS db,
    mf.name AS logical_file,
    mf.physical_name,
    mf.type_desc,
    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_ms,
    CASE WHEN vfs.num_of_writes > 0
         THEN vfs.io_stall_write_ms * 1.0 / vfs.num_of_writes ELSE 0 END AS avg_write_ms
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
ORDER BY avg_read_ms + avg_write_ms DESC;
-- 5. PAGEIOLATCH pressure pointing at the failing file
SELECT TOP 15 wait_type, waiting_tasks_count,
       wait_time_ms, signal_wait_time_ms,
       wait_time_ms - signal_wait_time_ms AS resource_wait_ms
FROM sys.dm_os_wait_stats
WHERE wait_type LIKE 'PAGEIOLATCH%'
ORDER BY wait_time_ms DESC;
# 6. List filter drivers in the I/O path (Windows, run elevated)
fltmc filters
fltmc instances

# 7. Disk and controller events from the Windows System log
wevtutil qe System /q:"*[System[Provider[@Name='disk'] or @Name='Ntfs']]]" /c:50 /rd:true /f:text

How to diagnose it

  1. Confirm the scope. Read the 823 or 824 message: which database, which logical file, which page ID, what kind of failure. One file named means localized; multiple files and databases mean suspect the controller, HBA, or host bus, not a single disk.

  2. Pull the durable record. Query msdb.dbo.suspect_pages. The number of rows, their event_type distribution, and the last_update_date tell you whether this is a fresh single event or ongoing degradation. A spike in rows over the last hour is an active hardware failure, not a historical artifact.

  3. Check database state. SUSPECT means the database hit corruption it cannot recover from during startup. RECOVERY_PENDING means the log could not be processed. Either state means the database is at least partially unavailable and you are now in restore territory, not triage.

  4. Run DBCC CHECKDB on the affected database. Use WITH PHYSICAL_ONLY first for speed: it skips logical checks but catches torn pages, checksum failures, and most allocation errors. If PHYSICAL_ONLY is clean, run a full check during a maintenance window. Capture output to a table or file, not just the console.

  5. Look at the storage layer. Windows System and Application event logs, the storage array’s own event log, hypervisor events for the underlying datastore, and SMART data via smartctl where available. You are trying to find the hardware fault that produced the bad I/O. If you do not, the replacement disk or restored database will fail again.

  6. Check the filter driver stack. Antivirus, backup agents, replication agents, and encryption minifilters all sit in the I/O path. fltmc filters enumerates them; fltmc instances shows which volumes they are attached to. Exclude SQL Server data, log, and backup file paths from real-time scanning. This is Microsoft’s documented guidance for 824 investigation.

  7. Look for VM snapshots. If the database is on a VM, check the hypervisor snapshot tree. A running SQL Server on a snapshotted disk, or one being consolidated, can return stale or inconsistent pages. Consolidate or remove snapshots on production databases only during a maintenance window.

  8. Decide restore vs repair vs page restore. If CHECKDB confirms corruption, restore from the most recent known-good backup is the default. Page-level restore is appropriate when only a handful of pages are affected and the database is in the full or bulk-logged recovery model with an unbroken log chain. Automatic page repair applies only in AlwaysOn Availability Groups or database mirroring, and only for errors 823 (when the OS returned a CRC error), 824, 829, and 832. It cannot repair the file header page (page ID 0) or the boot page (page ID 9).

Metrics and signals to monitor

SignalWhy it mattersWarning sign
Error 823 (severity 24)Hard OS-level I/O failureAny occurrence: storage fault in progress
Error 824 (severity 24)Logical consistency failureAny occurrence: corruption detected
Error 825Read retry succeededDisk deteriorating; canary before 823/824
msdb.dbo.suspect_pages new rowsDurable record of I/O integrity eventsAny new row since last check
Database state (sys.databases.state_desc)Database availabilitySUSPECT or RECOVERY_PENDING
I/O stall per file (sys.dm_io_virtual_file_stats)Latency as seen by the engineSustained > 20 ms reads, > 5 ms log writes
PAGEIOLATCH_* waitsEngine waiting on page reads from diskRising share of total wait time
PAGE_VERIFY optionWhether checksums are even being computedAnything other than CHECKSUM on user databases
Windows disk and controller eventsStorage-layer fault sourceNew disk, Ntfs, or controller events
Backup freshness and restore readinessYour only safe recovery pathNo tested restore in the last quarter

Fixes

Restore from a known-good backup (preferred)

This is the default path whenever CHECKDB confirms corruption. Restore the most recent full backup, the latest differential if any, and the transaction log chain up to just before the corruption event. If the database is still writable, take a tail-log backup first so you do not lose committed work between the last log backup and the failure. This recovers to a known-consistent state. Validate the restore with another CHECKDB before bringing the database back online.

Tradeoff: data loss is bounded by your recovery point objective. If the corruption was introduced between the last good backup and the event, that window of work is gone. There is no safer alternative.

Page-level restore

When CHECKDB names only a few pages and you are in the full or bulk-logged recovery model with a complete log chain, restore only those pages with RESTORE DATABASE ... PAGE='...', then roll forward the log. The database can stay online for everything except the affected pages. This is also the standard recovery path in AlwaysOn AGs, where a healthy replica can serve the page through automatic page repair.

Tradeoff: requires an unbroken log chain back to the backup containing the good version of the page. A broken chain forces a full restore.

Automatic page repair (AlwaysOn AG or database mirroring only)

When a page fails with 823 (when the OS returned a CRC error), 824, 829, or 832 on a replica, the replica requests the page from its partner. If the partner can read the page cleanly, it sends it back and the page is replaced. This is transparent to applications and works while the database is online.

Limitations: the file header page (page ID 0) and the boot page (page ID 9) cannot be repaired this way. A page that is corrupt on both replicas falls back to manual restore.

DBCC CHECKDB with REPAIR_ALLOW_DATA_LOSS (destructive, last resort)

This is the nuclear option. REPAIR_ALLOW_DATA_LOSS will deallocate pages it cannot reconcile, which means rows go away. Microsoft’s documentation is explicit: this option can result in more data loss than restoring from a last-known-good backup. Use it only when no usable backup exists, when a backup would lose more than the repair, or when the database is so large that restore time is unacceptable and the affected pages are known to be reclaimable.

After any repair, run CHECKDB again, validate referential integrity, and reconcile against upstream sources where possible. Document exactly what was repaired and what was lost.

Fix the underlying storage

Restoring onto the same failing storage just produces the next 823 or 824. Whatever your restore path, address the root cause in parallel: replace the disk, controller, or HBA; remove the failing filter driver; consolidate stale VM snapshots; update firmware; or migrate the database files to a healthy LUN. Then restore into the healthy storage.

Prevention

  • PAGE_VERIFY CHECKSUM on every database. This is the default for new databases on modern SQL Server, but databases upgraded from very old versions may still carry TORN_PAGE_DETECTION or NONE. Verify with SELECT name, page_verify_option_desc FROM sys.databases; and fix any that are not CHECKSUM.
  • Run DBCC CHECKDB on a schedule. Weekly is a common cadence for production. Without it, corruption can sit undetected for weeks, which means your last known-good backup may also be corrupt.
  • Test restores. A backup that has never been restored is a hypothesis, not a recovery point. RESTORE VERIFYONLY validates media and readability but not logical integrity; perform periodic actual restore tests on a separate instance and run CHECKDB against them.
  • Alert on Error 825 as hard as on 823 and 824. The 825 read-retry warning is the most reliable predictor of an imminent hard failure. Most teams do not monitor it because the operation succeeded. By the time 823 or 824 fires, data may already be lost.
  • Monitor msdb.dbo.suspect_pages for new rows. Prune resolved rows (event_type 4, 5, 7) so the 1,000-row cap does not silently swallow new events.
  • Exclude SQL Server file paths from antivirus and other filter drivers. Real-time scanning of .mdf, .ldf, and .ndf files is a documented cause of 823 and 824.
  • Avoid running production databases on VM snapshots. Consolidate and remove snapshots during maintenance windows. Monitor for stale snapshots on datastores hosting SQL Server.
  • Pre-size data and log files and avoid routine autogrowth. Autogrowth is not a corruption cause, but it is an I/O stall cause that compounds the impact of any storage-layer problem.
  • Track I/O latency per file. A degrading disk usually shows rising latency before it starts returning errors. Per-file sys.dm_io_virtual_file_stats deltas, sampled regularly, surface this trend.

How Netdata helps

  • Per-second error log parsing surfaces 823, 824, and 825 as they hit the log, with PAGE alerts on first occurrence rather than after batch thresholds.
  • msdb.dbo.suspect_pages delta detection raises on any new row, including the event_type so you can distinguish a fresh checksum failure from a repaired page being tracked.
  • Per-file I/O latency from sys.dm_io_virtual_file_stats collected at per-second granularity reveals the failing file before errors propagate, and isolates the bad LUN when an 823 names a logical file.
  • PAGEIOLATCH_* wait trend correlates with the I/O stall series: a rising share of wait time on page reads alongside new 825 entries is the earliest signal that the storage stack is unreliable.
  • Database state monitoring flags SUSPECT and RECOVERY_PENDING the moment they appear, which is often the first user-visible symptom of corruption that CHECKDB will later confirm.
  • ML anomaly detection on I/O latency and wait statistics flags degrading storage before it crosses an absolute threshold, which is the same early-warning role 825 plays inside SQL Server.

Netdata’s Microsoft SQL Server monitoring with Netdata brings these signals together with per-second metrics and ML anomaly detection.