0X00000304

Fix ERROR_ROWSNOTRELEASED (0X00000304) Data Provider Error

SQL Server ODBC error when rows aren't released before fetching more. Usually a cursor or connection issue. Fix it fast with these steps.

You're running a query that pulls thousands of rows, and right in the middle of the loop, SQL Server throws ERROR_ROWSNOTRELEASED (0X00000304). The message says the data provider needs previously fetched data released before asking for more. Sound familiar?

I've seen this exact error on a client's order entry system—they were using an old VB6 app with an ODBC connection to SQL Server 2016. Every morning the first run would work, then the second would blow up with this error. The fix took me longer than it should have because the app code looked fine. The real culprit? A cursor that wasn't being closed properly.

Here's the deal. This error is almost always about cursors and connection settings, not your SQL query itself. Let me walk you through the three most common causes, starting with the one that fixes 80% of cases.

Cause #1: The Application Didn't Close the Previous Cursor

Most of the time, this error appears when your code fetches rows from a cursor, then tries to fetch more without releasing the first set. Think of it like borrowing a book from a library—you can't grab another until you return the first.

In practice, this happens when your code does something like:

-- Example that causes the error
DECLARE cur CURSOR FOR SELECT * FROM Orders
OPEN cur
FETCH NEXT FROM cur WHILE @@FETCH_STATUS = 0
BEGIN
    -- Some processing that tries to run another SELECT on the same connection
    EXEC sp_some_proc_that_fetches_more_rows
    FETCH NEXT FROM cur
END
CLOSE cur
DEALLOCATE cur

The sp_some_proc might itself open a cursor or even just do a simple SELECT that needs to return rows. If your connection doesn't support Multiple Active Result Sets (MARS), the server won't let you have two active result sets on the same connection. That's when you get 0X00000304.

The fix: Close the cursor before doing anything else on that connection. Or better yet, avoid cursors entirely if you can. Use a set-based approach with a temp table or a table variable:

-- Better approach: no cursor
SELECT * INTO #tmpOrders FROM Orders
-- Process rows from #tmpOrders

If you really need a cursor (rare), make sure to close and deallocate it in a finally block or equivalent. I've also seen code that forgets to close a cursor after an error—so wrap it in try-catch.

Quick check: search your code for DECLARE CURSOR or OPEN cursor, and verify every OPEN has a matching CLOSE and DEALLOCATE. That's the first thing to fix.

Cause #2: Multiple Active Result Sets (MARS) Not Enabled

If you're using ADO.NET or ODBC with SQL Server, you might be trying to run two queries at once on the same connection. Without MARS enabled, SQL Server only allows one active result set per connection. When you try to fetch from a second result set before finishing the first, you get this error.

I ran into this on a web app that used a single connection to get a list of customers, then inside the loop fetched orders for each customer. On SQL Server 2005, MARS was off by default, and the error drove the devs nuts.

The fix: Enable MARS in your connection string. For ODBC and ADO.NET, add MARS Connection=True:

Server=myServer;Database=myDB;User Id=myUser;Password=myPass;MARS Connection=True;

For older drivers (like SQL Server ODBC Driver 11 or earlier), you might need to use the MultipleActiveResultSets=True keyword in connection string if you're using OLE DB. But for ODBC specifically, check your driver version. SQL Server ODBC Driver 13 and later support MARS, but you have to enable it.

If you can't change the connection string (legacy app), then you're stuck with restructuring the code to process rows sequentially, which is actually better for memory anyway.

One caveat: MARS doesn't work with all operations. For example, you can't have two active commands with the same connection if one is doing an INSERT and the other is doing a SELECT in a transaction that wants to lock the same table. But for typical reading, it's fine.

Cause #3: Using the Wrong Cursor Type with ODBC

Sometimes the error comes from the cursor type you're using. If you're using a keyset or dynamic cursor, and you try to fetch more rows than the cursor can handle in one go, the server might complain. This is especially true with ODBC when you're using the default cursor library.

Here's a trigger I've seen: A C# app using SqlDataReader with CommandBehavior.SequentialAccess to read large text fields. If you don't read the entire field before calling NextResult() or Read() again, you get this error because the driver hasn't released the previous field data.

The fix: Use a forward-only, read-only cursor (the default for most drivers) and make sure you read the data completely. If you're using ADO.NET, avoid SequentialAccess unless you need it for large BLOBs—and if you do, read the entire value into a variable before moving to the next row.

Also, check your ODBC connection settings. In the ODBC Data Source Administrator, make sure you're not setting a cursor type that conflicts. Sometimes the SQL_CURSOR_TYPE is set to dynamic when you don't need it. Change it to forward-only for read-only operations.

If you're using SQL Server Management Studio (SSMS) and getting this error, it's usually because the query results are being returned in a grid that tries to fetch all rows. But that's rare—SSMS handles it internally.

Quick-Reference Summary

Trigger Cause Fix
Cursor inside a loop with other queries on same connection Cursor not closed before new fetch Close and deallocate cursor immediately after use; or rewrite to set-based logic
Multiple active result sets on one connection MARS disabled Add MARS Connection=True to connection string (ODBC/ADO.NET)
Reading large fields with SequentialAccess then moving to next row Field data not fully read Read entire field into a variable before Read() again, or remove SequentialAccess

That's the short version. Start with cause #1—it's the most common in my experience. If that doesn't fix it, check your connection string for MARS. And if you're still stuck, look at how you're reading the data—especially if you're using any special command behaviors.

I've seen this error pop up on everything from legacy VB6 apps to modern .NET Core services. The pattern is always the same: something's holding onto rows when it shouldn't. Release them, and you're golden.

If you're in a pinch and can't change code right now, one workaround is to use separate connections for different parts of your query. But that's a band-aid, not a fix. Do it right—close your cursors, enable MARS if you need it, and move on.

Related Errors in Windows Errors
0X0000207E Fix ERROR_DS_ATT_ALREADY_EXISTS 0x207E in AD 0XC00D11D3 Fix NS_E_WMP_LICENSE_RESTRICTS (0XC00D11D3) 0X00003613 Fix ERROR_IPSEC_IKE_INVALID_PAYLOAD (0x00003613) – Invalid Payload 0X000036CE Fix ERROR_SXS_XML_E_MISSINGQUOTE (0X000036CE) Manifest Error

Was this solution helpful?

EP
Erropedia Team
Tech Support Editors
The Erropedia editorial team researches and documents real-world tech errors from across Windows, Linux, macOS, networking, databases, cloud platforms, and more. Every solution is reviewed for accuracy and updated as software and systems evolve.