tl;dr: I seem to have fixed it in my local environment, and will submit a patch once I test it a bit more and decide it's working alright.
This is kind of a tricky case, because when you save the query, the Data Explorer isn't actually sending the contents of your query to be saved. Instead, it sends the name and description you provided along with the ID of the query that you had just executed, which is available since all queries are logged in the database. This is all well and good, provided that the ID sent matches the exact query you submitted. Unfortunately, this isn't always what happens.
When queries are submitted, the Data Explorer caches the resulting JSON response before sending it back to the client. To index this cached information in the database, it uses a hash of the execution SQL which, given the same input values, will always be the same irrespective of the input names. Queries which hit the cache do still have their name, description, and raw SQL updated (technically), but this update is done based on the normalized SQL hash, which won't be the same if the input names have changed.
This is why when you changed something else in the query, you saw better results. Since the query was effectively new (even if nothing consequential changed), it went down the non-cached-result code path and everything got saved as expected.
There are a few barriers to fixing this in a way that doesn't break more important things, but I think I have a reasonable solution. I need to test it a bit more thoroughly to make sure it hasn't gone and thrown a wrench in anything, but provided it's alright I'll get waffles or someone to take a look and see if they'll pull it into the Data Explorer.