Skip to main content

Fixed: "NullReferenceException" at MySqlCommand.ExecuteNonQueryInternalAsync After KILL QUERY

· 5 min read
Jagdish Kumawat
Founder @ Dewiride

A statement was stuck behind a row lock, so I stopped it with KILL QUERY. The mysql client reports that as ERROR 1317: Query execution was interrupted. My API got System.NullReferenceException from inside MySql.Data. It's a misleading message, and once you know that, it tells you exactly what happened.

What I Was Doing​

My .NET C# Web API uses EF Core 10.0.14 and MySql.Data 26.10.0. An ExecuteUpdateAsync was waiting for a row that another session held, so in a root session I found its connection and stopped the statement:

mysql: root session
KILL QUERY <connection id>;

What I Expected​

An error saying the statement was interrupted, which is what the mysql client shows:

Output (the mysql client)
ERROR 1317 (70100): Query execution was interrupted

What I Got​

Output (status, and how long it took)
curl exit 0, 1.728200 s, status 500
API log
Failed executing DbCommand (1,656ms) [Parameters=[@id='?' (DbType = Int32)], CommandType='Text', CommandTimeout='0']
System.NullReferenceException: Object reference not set to an instance of an object.
at MySql.Data.MySqlClient.MySqlCommand.ExecuteNonQueryInternalAsync(CancellationToken cancellationToken)

The request answering 500, with the log line System.NullReferenceException at MySqlCommand.ExecuteNonQueryInternalAsync.

What I Checked​

The driver's source. Two pieces of MySqlCommand.cs meet here. When the server says the statement was interrupted, the reader method returns null:

MySqlCommand.cs (Oracle's source, tag 26.10.0)
// if we caught an exception because of a cancel, then just return null
if (mySqlException.IsQueryAborted)
return null;

And ExecuteNonQueryInternalAsync uses what comes back without checking it:

MySqlCommand.cs (Oracle's source, tag 26.10.0, shortened)
using (MySqlDataReader reader = await ExecuteReaderAsync(default, cancellationToken).ConfigureAwait(false))
{
reader.Close();

What IsQueryAborted covers.

MySqlException.cs (Oracle's source, tag 26.10.0)
internal bool IsQueryAborted => (Number == (int)MySqlErrorCode.QueryInterrupted ||
Number == (int)MySqlErrorCode.FileSortAborted);

QueryInterrupted is error 1317, the one the mysql client showed.

Why It Happens​

The driver treats error 1317 as "the application cancelled this", and hands back null. ExecuteUpdateAsync goes through ExecuteNonQueryInternalAsync, which then calls reader.Close() on that null. So the real error is swallowed, and you get a NullReferenceException in its place.

The statement really was stopped, and nothing changed. Only the message is wrong.

The Fix​

There's no setting for this. What you can change is how you read it, and what you run.

Read it as "someone ran KILL QUERY". If this stack shows up in your logs and your code hasn't changed, look for whoever ran it: a person, or a tool that stops slow statements.

If you have to stop one of the API's statements, use KILL. That ends the whole connection, and the driver reports that properly, as a dropped connection. A retry strategy can recognise that:

mysql: root session
KILL <connection id>;

After the Fix​

Output (status, and how long it took)
{"changed":1}
curl exit 0, 3.599492 s, status 200
API log
A transient exception occurred during execution. The operation will be retried after 0ms.

The request answering 200 after a KILL, with the log line A transient exception occurred during execution. The operation will be retried after 0ms.

The retry ran the statement again, and by then the row was free. That's the catch with this route: with a retry strategy, KILL stops this attempt, and the statement runs again. If it must not run at all, find the request that sends it.

Common Mistakes​

MistakeWhat happensDo this instead
Hunting for a null in your own codeThe null is inside the driverRead the top frames of the stack
Adding NullReferenceException to your retryYour own bugs get retried tooLeave it out
Assuming the statement did somethingIt was stopped, and changed nothingCheck the row if you need to be sure

Troubleshooting​

SymptomLikely causeFix
The stack ends in NativeDriver.ExecutePacketAsyncA retry on a broken connectionThe fix
Fatal error encountered during command executionKILL, or a dropped connectionThe fix
Nobody ran KILL QUERYA tool didLook in the general query log

Conclusion​

With MySql.Data, KILL QUERY on an ExecuteUpdateAsync shows up as a NullReferenceException, because the driver returns a null reader for error 1317 and then uses it. Nothing changed in the database. Use KILL when you have to stop one, and let the retry strategy deal with it.

Additional Resources​

Stay Updated

Subscribe to our newsletter for the latest tutorials, tech insights, and developer news.

By subscribing, you agree to our privacy policy. Unsubscribe at any time.