Skip to content
Microsoft SQL Server

SQL Server Error 601

Could not continue scan with NOLOCK due to data movement

Applies to: All supported SQL Server versions (2016 to 2025), Azure SQL Database, Azure SQL Managed Instance
Last technically reviewed 24 October 2026 on SQL Server 2025 (17.0.1145.4)
The full message
Could not continue scan with NOLOCK due to data movement.
Error number
601
Severity
12 (user-correctable error)
State
Not documented by Microsoft (not reproduced here)
Product
Microsoft SQL Server Database Engine
Versions
All supported SQL Server versions (2016 to 2025), Azure SQL Database, Azure SQL Managed Instance
Last reviewed
24 October 2026

What it means

Error 601 means a query reading with NOLOCK or READ UNCOMMITTED lost its place because another transaction moved or removed the rows it was scanning, so SQL Server cancelled the query; it is a concurrency problem, not damage on disk.

Normally a reader waits for, or is protected from, rows that another transaction is changing. WITH (NOLOCK) and SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED remove that protection: the scan reads pages while other sessions split, move or deallocate them. If the page the scan needs next has gone away or now holds something else, the engine cannot continue the scan safely and cancels the query with 601.

The word "NOLOCK" in the message is the important part: it tells you the query chose dirty reads. Severity 12 is the level reserved for queries that do not lock because of query hints, and the data on disk is fine. The same mechanism can also produce transient error 605 at severity 12. Retrying usually works, but the same dirty reads that cause 601 can also silently return rows twice, skip rows or return uncommitted values, so the real fix is to stop reading uncommitted data.

Common causes

  • A report or application query uses WITH (NOLOCK) on a busy table while other sessions insert, delete or update rows.
  • A connection or procedure sets TRANSACTION ISOLATION LEVEL READ UNCOMMITTED, so every table it reads behaves as if NOLOCK were used.
  • Index rebuilds, page splits from inserts into the middle of an index, or large deletes that move or deallocate pages during the scan.
  • Long scans of large tables, which give concurrent changes more time to move the pages ahead of the scan.

Diagnose it

Read-only These queries only read metadata and dynamic management views. They do not change data, permissions or configuration, so they are safe to run first.

Which sessions read uncommitted data right now?

SELECT s.session_id, s.login_name, s.program_name, s.host_name,
       s.transaction_isolation_level
FROM sys.dm_exec_sessions AS s
WHERE s.is_user_process = 1
  AND s.transaction_isolation_level = 1;  -- 1 = READ UNCOMMITTED
What to look for

Applications that run whole sessions in READ UNCOMMITTED. NOLOCK hints inside queries do not show here; see the next check.

Which procedures, views and functions use NOLOCK?

SELECT OBJECT_SCHEMA_NAME(m.object_id) AS schema_name,
       OBJECT_NAME(m.object_id) AS object_name, o.type_desc
FROM sys.sql_modules AS m
JOIN sys.objects AS o ON o.object_id = m.object_id
WHERE m.definition LIKE N'%NOLOCK%'
   OR m.definition LIKE N'%READUNCOMMITTED%'
   OR m.definition LIKE N'%READ UNCOMMITTED%';
What to look for

The modules to change. Match them to the query that failed (the application log usually records its text).

Which cached queries use NOLOCK?

SELECT TOP (20) qs.execution_count, qs.last_execution_time,
       SUBSTRING(t.text, 1, 400) AS query_text
FROM sys.dm_exec_query_stats AS qs
CROSS APPLY sys.dm_exec_sql_text(qs.sql_handle) AS t
WHERE t.text LIKE N'%NOLOCK%'
ORDER BY qs.execution_count DESC;
What to look for

Ad hoc and application queries in the plan cache that use the hint, most frequently run first.

Is row versioning already available in the database?

SELECT name, is_read_committed_snapshot_on, snapshot_isolation_state_desc
FROM sys.databases
WHERE database_id = DB_ID();
What to look for

If is_read_committed_snapshot_on = 1 (the default in Azure SQL Database), ordinary READ COMMITTED reads already avoid blocking and NOLOCK gains nothing. ON for snapshot isolation means sessions can use SNAPSHOT straight away.

How to fix it

Each fix is labelled with what it changes. Try read-only checks and the least invasive fix first, and test changes outside production.

Retry the query once

Client or network side

Microsoft notes that the query can simply be resubmitted; the data is not damaged. Use this as an immediate workaround only, because the dirty reads that caused 601 are still there.

Read committed data with snapshot isolation instead of NOLOCK

Changes configuration

Snapshot isolation gives the reader a consistent view of committed data from row versions, without taking shared locks and without being blocked by writers. Allow it in the database, then switch the reporting session to it and remove the NOLOCK hints from its queries.

Caution: Row versioning stores versions in tempdb (or in the database with accelerated database recovery) and adds a few bytes to changed rows. Check tempdb space and test under load before enabling it in production.

ALTER DATABASE AppDb SET ALLOW_SNAPSHOT_ISOLATION ON;

-- in the reporting session or procedure
SET TRANSACTION ISOLATION LEVEL SNAPSHOT;
SELECT CustomerId, SUM(Amount) AS Total
FROM dbo.Orders           -- no NOLOCK hint
GROUP BY CustomerId;

Or make READ COMMITTED use row versions for every query

Changes configuration

READ_COMMITTED_SNAPSHOT changes the default isolation level for the whole database to read committed row versions, which removes most of the blocking that NOLOCK was added to avoid, without changing queries. After that, remove the NOLOCK hints.

Caution: Turning on READ_COMMITTED_SNAPSHOT needs a moment with no other active connections in the database (WITH ROLLBACK IMMEDIATE rolls back their open transactions), and code that relies on readers being blocked can behave differently. Test first and do it in a maintenance window.

ALTER DATABASE AppDb SET READ_COMMITTED_SNAPSHOT ON WITH ROLLBACK IMMEDIATE;

Remove NOLOCK and READ UNCOMMITTED from the code

Changes schema

Alter the procedures and views found by the diagnostics, and the application queries, so they use the default READ COMMITTED (or SNAPSHOT). If blocking is what NOLOCK was hiding, fix the blocking itself: shorter write transactions and indexes that let readers touch fewer rows.

Examples

A NOLOCK report scan while orders are being purged

Shown as documented by Microsoft; not reproduced here (a two-session attempt on SQL Server 2025 completed hundreds of NOLOCK scans without 601). The level and state a client sees were not observed, so only the message text is shown.

-- Session 1 (report)
SELECT COUNT(*) FROM dbo.Orders WITH (NOLOCK);

-- Session 2, at the same time (purge job)
DELETE TOP (50000) FROM dbo.Orders WHERE OrderDate < '20200101';
Result
Could not continue scan with NOLOCK due to data movement.

How to prevent it

Do not use NOLOCK or READ UNCOMMITTED as a general speed-up. Where readers and writers block each other, use READ_COMMITTED_SNAPSHOT or snapshot isolation, which give consistent results without blocking, and keep write transactions short.

Add code review checks for NOLOCK hints and isolation level changes, and run large purges and index maintenance at times when long reports are not scanning the same tables.

Common questions

Is error 601 a sign of corruption?

No. It is raised because a dirty-read scan lost its position, and the data on disk is intact. If the same query also fails without NOLOCK, or DBCC CHECKDB reports errors, investigate corruption separately.

If I add retry logic, can I keep NOLOCK?

You can, but the retry only hides 601. NOLOCK can still return rows twice, miss rows and return values that are later rolled back, without any error. Snapshot isolation avoids both problems.

Does READ_COMMITTED_SNAPSHOT make NOLOCK unnecessary?

Yes for blocking: readers no longer wait for writers. A NOLOCK hint still overrides it and reads uncommitted data, so remove the hints as well.

Learn more on CodeWithSQL

Official documentation

Last technically reviewed 24 October 2026; examples and diagnostics run on SQL Server 2025 (17.0.1145.4). Message text checked against the engine's sys.messages catalogue.

Still stuck on a SQL Server error?

Browse the full SQL Server error library, or get hands-on help from an experienced SQL professional.