Skip to content

[FIXED] Instant crash on launch - #1416

Closed
pepsisnothanku wants to merge 5 commits into
Jellify-Music:mainfrom
pepsisnothanku:fix/track-change-crash-and-font-weights
Closed

[FIXED] Instant crash on launch#1416
pepsisnothanku wants to merge 5 commits into
Jellify-Music:mainfrom
pepsisnothanku:fix/track-change-crash-and-font-weights

Conversation

@pepsisnothanku

Copy link
Copy Markdown

What is the change

Three independent Android bug fixes, one per commit:

  1. fix: correct Figtree weight tokens and font face mapping — corrects the weight→face map in src/configs/styling/fonts.ts and fills the weight token scale out to $1$10.
  2. fix: prevent crash on track change from gesture detector child count — wraps the animated subtrees inside GestureDetector in the player header and mini player with a stable, non-collapsible View.
  3. fix: return null from lyrics fetch when a track has no lyricsfetchRawLyrics now returns null instead of undefined.

What does this address

1. Invalid fontWeight values from unresolvable weight tokens

  • The weight scale defined only $4, $6 and $8. A weight token with no entry falls through and resolves against a different token scale, producing values such as fontWeight: 32, which is not a legal CSS weight. On the new architecture this surfaced as a crash on the library selector (fontWeight={isSelected ? '$8' : 'unset'}) rather than a silent fallback. Filling the scale to $1$10 means no token can miss.
  • Separately, the face map was shifted one step heavy from 300 upward — 400 resolved to Figtree-Medium, 600 to Figtree-Bold, 800 to Figtree-Black. Every italic entry also used spaces rather than hyphens ('Figtree Bold Italic' instead of 'Figtree-BoldItalic'). Since Android resolves fontFamily by asset filename, no italic face has ever matched a bundled font — all italics silently fell back to the system typeface.
  • Note this slightly lightens text at $6 and $8, which is the corrected weight rather than a regression.

2. Crash on every next-track press (Android)

  • react-native-gesture-handler 3.x asserts that a GestureDetector owns exactly one child view when native handlers are attached (RNGestureHandlerDetectorView.addView, line 73). Reanimated's exiting layout animations intentionally keep the outgoing view mounted while the incoming one is added, so an animated direct child briefly gives the detector two children:
    java.lang.AssertionError: Cannot have more than one child view when native gesture handlers are attached to the detector at com.swmansion.gesturehandler.react.RNGestureHandlerDetectorView.addView(RNGestureHandlerDetectorView.kt:73) at com.facebook.react.fabric.mounting.SurfaceMountingManager.addViewAt(SurfaceMountingManager.kt:381) ... at com.swmansion.reanimated.NativeProxy.performOperations(Native Method)
  • In Player/components/header.tsx, the album art is conditional, keyed on the track ID, and has both entering and exiting animations — so this reproduced on every next-track press. The dispatch originating from NativeProxy.performOperations rather than React's commit path is what identifies it as the layout animation rather than an ordinary re-render.
  • Wrapping the animated subtree in a stable View gives the detector a single child that is never replaced; the animation swap now happens one level below it. collapsable={false} is required so Android's view flattening doesn't remove the wrapper again. The mini player has the same pattern with an entering animation and is fixed identically. The other five GestureDetector call sites already wrap their animated content in a stable parent and are untouched.
  • One caveat worth flagging: Kotlin's assert is gated on Class.desiredAssertionStatus(), so this hard crash may only surface in debuggable builds. The underlying condition — native handlers attaching to a transient child — is a real defect regardless of whether the assertion fires.
  1. Query data cannot be undefined on tracks without lyrics
  • fetchRawLyrics returned undefined both on request failure and when the server responded without a Lyrics field. TanStack Query rejects undefined as a result, logging Query data cannot be undefined. Affected query key: ["TRACK_LYRICS", ...] and leaving the query unresolved — so any track without lyrics produced a console error on every play.

Testing

  • bun tsc — 0 errors
  • bun lint — 0 errors
  • bun format:check — clean
  • bun test — 122/122 passing
  • Manually verified on a Pixel 9 (LineageOS, Android 16): next-track no longer crashes, and the screens using $6/$8 weight tokens render correctly with tokens restored.

Issue number / link

Issue: #1415
Link: #1415
This person had the same issue also on their Pixel 11. My issue was on a Pixel 9; this PR could solve both issues:
Issue: #1413
Link: #1413

Tag reviewers

@anultravioletaurora

pepsisnothanku and others added 5 commits August 28, 2026 21:10
react-native-gesture-handler 3.x asserts that a GestureDetector owns exactly
one child view when native handlers are attached, in
RNGestureHandlerDetectorView.addView. Reanimated's exiting layout animations
intentionally keep the outgoing view mounted while the incoming one is added,
so an animated direct child briefly gives the detector two children:

  java.lang.AssertionError: Cannot have more than one child view when native
  gesture handlers are attached to the detector

In the player header the album art is conditional, keyed on the track ID, and
has both entering and exiting animations, so this reproduced on every
next-track press on Android. Wrapping the animated subtree in a stable,
non-collapsible View gives the detector a single child that is never replaced;
the animation swap now happens one level below it. collapsable={false} is
required so Android's view flattening does not remove the wrapper again.

The mini player uses the same pattern with an entering animation and is fixed
the same way. The five other GestureDetector call sites already wrap their
animated content in a stable parent and are unaffected.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
We found two problems in the font config:

The face map was shifted one step heavy from 300 upward, so 400 resolved to
Figtree-Medium, 600 to Figtree-Bold and 800 to Figtree-Black. Every italic
entry also used spaces rather than hyphens ('Figtree Bold Italic' instead of
'Figtree-BoldItalic'). Android resolves fontFamily by asset filename, so no
italic face ever matched a bundled font and all italics silently fell back to
the system typeface.

The weight scale defined only $4, $6 and $8. A weight token with no entry in
the scale falls through and resolves against a different token scale,
producing values such as fontWeight: 32, which is not a legal weight on Android. On the new architecture this surfaced as a crash on the library selector rather than a silent fallback.

Fills the weight scale to $1-$10 so no token can miss, and corrects the face
map to the standard CSS weight names. Reverts the '$8' -> '$6' workaround in
library-selector, which is no longer needed now that $8 resolves to a real
font file.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>From me (pepsisnothanku): I'm really greatful Claude right now! I couldn't have solved these issues without its help. I know AI is a touchy subject right now, but I've been using it to help teach me to code, and I genuinely could not have figured this out without its help. And now it works! I'm so happy!
fetchRawLyrics returned undefined both when the request failed and when the
server responded without a Lyrics field. TanStack Query rejects undefined as a
query result, logging "Query data cannot be undefined" and leaving the query
unresolved, so a track without lyrics produced a console error on every play.

Returns null in both cases and narrows the return type to match.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…al fontWeight. While it was fine for debugging, it crashed on startup in the release build. These commits fix that, letting the release build FINALLY work independently, all features included. It still needs optimizations and has bugs, but those are now added to the comments.

-keep class com.jellify.BuildConfig { *; }

# KNOWN GAP: these rules are thin for an app with this many native modules, and R8

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this change necessary, or just leftover from Claude?


/**
* Fetch raw lyrics text for a given track item.
*

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this change is relevant

}

const close = () => {
// TODO: leftover debug logging. These [SR] logs fire on every swipe open/close and

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this relevant to the startup crashes?

captionAlign = 'center',
...cardProps
}: CardProps) {
// TODO: remove this call. ItemCard is a list item — it renders once per album /

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

usePerformanceMonitor is a no-op in release mode.

Is this change relevant to fixing crashes?

import React from 'react'
import { useTheme, View, YStack, ZStack } from 'tamagui'
import { useWindowDimensions } from 'react-native'
// NOTE: this file is the ONLY consumer of react-native-linear-gradient in the app.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this relevant to the startup crashes?

const customBlurhash = typeof payload?.blurhash === 'string' ? payload.blurhash : undefined

return (
/*

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this is relevant to the startup crashes

import { ListRenderItemInfo, Platform } from 'react-native'
import { useSafeAreaFrame, useSafeAreaInsets } from 'react-native-safe-area-context'
// NOTE: react-native-drax@1.1.0 declares `react-native-gesture-handler: ">=2.0.0"` and
// has no stated support for RNGH 3.x, which this app runs. RNGH 3 was a major rewrite

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

v3 recommended is literally on the react-native-drax website

https://nuclearpasta.com/react-native-drax/getting-started

Comment thread ANDROID-NOTES.md
@@ -0,0 +1,104 @@
# Android debugging notes

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we remove this AI generated file?

Comment thread App.tsx
import { ReducedMotionConfig, ReduceMotion } from 'react-native-reanimated'
import { useOtaUpdate } from './src/hooks/ota'

// NOTE: this hides the LogBox overlay but does NOT make logging free. Console calls

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's your source on this?

Comment thread App.tsx
const handleRetry = () => setReloader((r) => r + 1)

return (
/*

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't need this here

@pepsisnothanku

pepsisnothanku commented Sep 1, 2026

Copy link
Copy Markdown
Author

@anultravioletaurora
I discovered that these changes only solve the problem in developer mode. They also allowed the app to run smoothly without constantly crashing on my Pixel 9, especially when trying to skip to the next song. There was a race condition or two causing the program to crash when skipping songs, so I fixed that and added some optimizations to make the program run a little faster/smoother on my device.

But the actual solution to the problem was with fontWeight; I had to manually declare all the fontWeights. I discovered that the other day. THAT'S what solved the app-crashes-on-opening problem; I just haven't committed it yet. The fontWeight thing made the app actually functional on my device, but it was so buggy it was completely unusable. The rest of the edits that you see on this commit I made targeted that.

And I will go through all of these and modify the ai-specific language. Sorry about that; this is really my first time ever contributing to a project on github, so AI is really holding my hand right now. But you asked me to check the relevance of the changes; I'll go through it all and explain what was and wasn't important and why. Sorry for the inconvenience and thanks for bearing with me as I get used to this 😅😊

@anultravioletaurora

Copy link
Copy Markdown
Member

Sorry for the inconvenience and thanks for bearing with me as I get used to this 😅

OMG no worries! I appreciate you making the effort to fix it regardless 🙇‍♀️

@riteshshukla04 got this fixed in #1428 - which has since been merged and released as 1.2.10, so I'm going to close this PR since we've got the issue fixed...

...BUT don't be discouraged - and do let me know if there is anything you need from me to create other PRs 😇

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants