Skip to content

fix/DynamicFontModule - double callback invocation causing SIGABRT on Android#4030

Open
mika-bejerano wants to merge 1 commit into
wix:masterfrom
mika-bejerano:fix/dynamic-font-module-double-callback
Open

fix/DynamicFontModule - double callback invocation causing SIGABRT on Android#4030
mika-bejerano wants to merge 1 commit into
wix:masterfrom
mika-bejerano:fix/dynamic-font-module-double-callback

Conversation

@mika-bejerano

Copy link
Copy Markdown

Description

DynamicFontModule.loadFont (Android) had a catch/finally structure where both blocks invoked the React Native callback, causing a fatal crash when an exception occurred during font loading.

Root cause:

// BEFORE (buggy)
} catch(Exception e) {
    callback.invoke(e.getMessage());  // invoked on exception
} finally {
    callback.invoke(null, name);      // ALWAYS invoked — crashes when catch also ran
}

React Native enforces that native callbacks are invoked exactly once. When any exception is thrown during font loading (e.g. Typeface.createFromFile failing on certain Android devices), both catch and finally fire, invoking the callback twice and triggering a fatal SIGABRT:

Abort message: 'Callback arg cannot be called more than once'

Fix: Move the success callback into the try block and remove the finally. The callback is now invoked exactly once — success path inside try, error path inside catch.

// AFTER (fixed)
      cacheFile.delete();
      callback.invoke(null, name);    // success only
    } catch(Exception e) {
      callback.invoke(e.getMessage()); // error only
    }
    // no finally

Changelog

DynamicFontModule (Android) — fixed intermittent fatal crash (SIGABRT) caused by the native callback being invoked twice when an exception occurred during loadFont. The app would crash ~4 seconds after launch before any UI interaction.

Additional info

  • Crash signature: signal 6 (SIGABRT), Abort message: 'Callback arg cannot be called more than once', stack trace pointing to DynamicFontModule.loadFont
  • Intermittent — only reproduces when an exception is thrown mid-load (e.g. on certain cloud/real devices where Typeface.createFromFile or file I/O fails transiently)
  • Only affects apps that load custom fonts via DynamicFonts on startup (branded apps); standard apps are unaffected
  • Note: the commented-out loadFontFromFile method in the same file already uses the correct pattern (a wasLoaded flag guarding the finally) — this fix aligns loadFont with that same intention, simplified

… Android

The `finally` block unconditionally called `callback.invoke(null, name)` after
every execution path, including after the `catch` block already invoked the
callback on error. React Native enforces single-use callbacks and throws a fatal
SIGABRT when a callback is invoked more than once.

Fix: move the success callback into the `try` block and remove the `finally`.
The callback is now invoked exactly once — on success inside `try`, or on
failure inside `catch`.

Reproduces intermittently on Android (branded apps) when an exception is thrown
during font loading (e.g. `Typeface.createFromFile` failing on certain devices),
causing the app to crash ~4 seconds after launch before any UI interaction.
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