This ended up as a much bigger post than I intended, but I think I am at least close to a root cause here. Sorry for the long read!
I extracted index.js from the app.asar file, deminified it, and put it back in place so I could better follow the traceback.
It seems like it is trying to compute the value of something that is set to be “observable”, and due to the later call to pickBestCircularGradientColors and the info message just before the stack trace saying that it is Setting new palette and loudness data, I strongly suspect that what is being computed is the gradient for the background that is based on the color palette in the album art. I also see WARNING - IMAGE: No palette to be extracted as there is no thumb. which could be related, but it’s hard to say.
The summary of what I have found so far is that computeValue calls Me, passing it an object this. I can’t manage to attach a debugger to the app, so I can’t figure out what this is when I hit this failure.
Me(this, this.derivation, this.scope);
Me then does some checks and then tries to run t.call
if (((Re.trackingDerivation = e), !0 === Re.disableErrorBoundaries)) r = t.call(n);
else
try {
r = t.call(n); // We are hitting this
} catch (e) {
r = new we(e);
}
which eventually gets to this function:
key: "gradientColors",
get: function () {
var e = this.metadata && this.palette ? (0, k.pickBestCircularGradientColors)(this.palette) : ["#222", "#222", "#222", "#222"];
The first part of that ternary is evaluating to something truthy, because the traceback mentions this as the part that is failing:
(this.palette)
That, again, appears to make it pretty far through the call stack before getting to pickBestCircularGradientColors:
pickBestCircularGradientColors: function (e) {
if (!e) return null;
if (e.length < 20) {
var t = e.length;
(e.length = 20), e.fill("#ddd", t, 20);
}
The stack trace here indicates that this is failing at the = in e.length = 20, which seems pretty odd to me, but I’m not a js developer. Apparently this assignment somehow eventually calls down to the set function defined for the prototype object here:
Object.defineProperty(mn.prototype, "length", {
enumerable: !1,
configurable: !0,
get: function () {
return this.$mobx.getArrayLength();
},
set: function (e) {
this.$mobx.setArrayLength(e); // We are failing here
},
That setArrayLength function then calls down to spliceWithArray
(e.prototype.setArrayLength = function (e) {
if ("number" != typeof e || e < 0) throw new Error("[mobx.array] Out of range: " + e);
var t = this.values.length;
if (e !== t)
if (e > t) {
for (var n = new Array(e - t), r = 0; r < e - t; r++) n[r] = void 0;
this.spliceWithArray(t, 0, n); // Hitting this one
} else this.spliceWithArray(e, t - e);
}),
which in turn calls ke(this.atom)
(e.prototype.spliceWithArray = function (e, t, n) {
var r = this;
ke(this.atom);
ke is a pretty short function but seems to be failing at the first call to p(!1):
function ke(e) {
var t = e.observers.length > 0;
Re.computationDepth > 0 && t && p(!1), Re.allowStateChanges || (!t && "strict" !== Re.enforceActions) || p(!1);
}
p is also a pretty small function:
function p(e) {
throw (h(!1, e), "X");
}
which finally brings us to the top of the call stack at h:
function h(e, t) {
if (!e) throw new Error("[mobx] " + (t || u));
}
It appears that in my case, h is being called with h(!1, !1) which should always return whatever u is, in this case it appears to be
An invariant failed, however the error is obfuscated because this is an production build.
I assume t would not be !1 in a dev build and that is how you get real debugging internally. p and h appear to just be methods used to raise exceptions, so they can probably be ignored, so my best guess is an issue with how the computationDepth is being computed in ke.
The album art for the album that is trying to play (and seems to be causing this issue, because it is happening on app startup right now) doesn’t exist. Realizing that it may be related to the specific album prompted me to try this in the mobile version of the app, where I was just able to replicate the issue.
Manually looking up and fetching the album art, adding it to the directory with the music, getting plex to update it’s info to show the album art, force closing the plexamp mobile app, and then re-launching it seems to have fixed the black/grey-screen hang. Dumping the cache directory and PlayQueue.json from the AppData\Local\Plexamp\Plexamp directory and then relaunching seems to have fixed it on the windows side.
The root cause seems to be an error in generating the color palette based on the album art in the case where the album art doesn’t exist. It looks like there are a bunch of places where this should be caught and other colors should be substituted, so it isn’t clear to me why sometimes it generates a greyscale gradient and other times I get this issue.