"Failed to execute 'transaction' on 'IDBDatabase': The database connection is closing." – what?

Part I: the error itself

I just got this error in my app:

{
  "error": {
    "name": "InvalidStateError",
    "code": 11,
    "reason": "Failed to execute 'transaction' on 'IDBDatabase':
The database connection is closing.",
    "line": 231,
    "column": 2914,
    "sourceURL": "https://myapp.com/0db724f67acbb9327cf274e82c9127fd7809b631.js?
meteor_js_resource=true",
    "stack": [
      "transaction@[native code]",
      "https://myapp.com/0db724f67acbb9327cf274e82c9127fd7809b631.js?
meteor_js_resource=true:231:2914",
      "https://myapp.com/0db724f67acbb9327cf274e82c9127fd7809b631.js?
meteor_js_resource=true:1:8267",
      "promiseReactionJob@[native code]"
    ]
  }
}

(domain name changed)

The user agent was as follows:
Mozilla/5.0 (iPad; CPU OS 13_7 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.1.2 Mobile/15E148 Safari/604.1

The above indicates an iPad (iPadOS 13.7 is of Sep 9, 2020).

While my app performed a dynamic import, the error was thrown from within Meteor core exactly here:

The app is now out there for about 1.5 years, and this is the first time that I see this error.

I did google and found very little.

On github, related to firebase-js-sdk on 4 Mar 2020:

It looks like you are experiencing something akin to #1533. The good news is that you are not on your own, the bad news is that this seems to be a longstanding issue that is caused by unexpected behavior in Mobile Safari that we haven’t been able to completely work around.

Does it ring a bell with anybody? Any ideas?

Part II: finding where errors actually occur

This issue also leads to another side question: how do you guys find out about errors in production where they actually occurred, such as module, line and column? Stack traces refer to minified code, obviously. I’ve been struggling with this for very long now. @zodern 's minifier is brilliant, but it only creates sourcemaps for the app’s own code, not for anything in node_modules or in Meteor.

Here’s what I did: I wrote a little utility that gets a stacktrace url as an argument. It fetches the content (which is in production the app.js), picks the line, navigates to the column, and displays the surrounding of the reported location, like this:

Note: line 231 / column 2914 is the location of interest:

$ node lookupCodeLocation.js https://escortize.com/0db724f67acbb9327cf274e82c9127fd7809b631.js?meteor_js_resource=true:231:2914

…and the output is:

Each((function(e){a.createObjectStore(e,j[e])}))},d.onerror=r(a,“indexedDB.open”),d.onsuccess=function(a){e(a.target.result)}}))).then(e,(function(a){return e(null)}))}var j={sourcesByVersion:{keyPath:“version”}};function r(e,a){return function©{return e(new Error("IndexedDB failure in “+a+” "+JSON.stringify(c.target))),!0}}var i=0;d.checkMany=function(e){var a=Object.keys(e),c=Object.create(null);return a.forEach((function(e){c[e]=null})),s?t((function(d){if(!d)return c;var b,s=d.transaction ***!!! THE ERROR IS RIGHT HERE --> ([“sourcesByVersion”],“readonly”).objectStore(“sourcesByVersion”);function n(){return–i,c}return++i,f.all(a.map((function(a){return new f((function(d,f){var b=e[a];if(b){var n=s.get(b);n.onerror=r(f,“sourcesByVersion.get”),n.onsuccess=function(e){var f=e.target.result;f&&(c[a]=f.source),d()}}else d()}))}))).then(n,n)})):f.resolve©};var o=Object.create(null);function l(){if(i>0)return l.timer=setTimeout(l,100);l.timer=null;var e=o;return o=Object.create(null),t((function(a){if(a){var c,d=a.tran

The eye-catcher !!! THE ERROR IS RIGHT HERE --> is actually in the output, so that it’s easy to spot the concrete location where the error was reported. In this instance I did a full text search for "IndexedDB failure in" (it’s in that jungle above) in node_modules, and that quickly gave away the module dynamic-import.js in Meteor.

Poking around like this in minified code leaves me with with a sickening feeling that I’m doing something very wrong in an otherwise cutting edge technology setting.

Any comments?

We are also experiencing this. And the alarming part is that we now have 2 proven cases wherein the client was not able to recover. The dynamically imported file/component was broken in the client and seems like the client cached it.

The only solutions we found for the client to get out of this case are:

  1. Clear the local storage (which is difficult for a regular user)
  2. We deploy a new build

We still do not have a solution to this

1 Like

Wiping the dynamic import cache on the client’s device seems to be the solution. With react-loadable there is this “loading” component that also get an error property. Normally a component should be rendered that informs the user that something went wrong and offer a button to retry. It is quite questionable if a retry can solve a situation when the cache becomes corrupt. Adding a [ RESTART APPLICATION ] button seems like a good idea. The deletion of the cache requires as much as this:

window.indexedDB.deleteDatabase("MeteorDynamicImportCache");
window.location.reload();

The mere invoking deleteDatabase() is not enough, the app needs to be reloaded, or the tab or window closed, for the browser to actually execute the deletion. But I guess this is ok as a last resort, as long as the decision is made by the user. That’s still pretty bad, all transient data will be lost in the app, but there is at least a chance to resume using it, after all.

P.S. indexedDB’s API is sheer horror.

1 Like

Nice idea. Will definitely implement this :+1:

1 Like

Implemented this and have no 100% way to test if this will work as we cannot replicate the error ourselves.

An iPad with iPadOS around 13.7 appears to be a viable candidate device for testing, if someone in your teams has access to one. As I wrote, I have a documented case of such a device causing this error twice in a row.