Skip to content

[Exploratory] WS settings - App crashes after importing a multi level tag file #100367

Open

Description

@applause-bot

If you haven’t already, check out our contributing guidelines for onboarding. To join our Slack channel, fill out this form.


Version Number: 9.4.70-0
Reproducible in staging?: Yes
Reproducible in production?: Yes
If this was caught during regression testing, add the test name, ID and link from BrowserStack: https://test-management.browserstack.com/projects/2219752/folder/53774965/test-cases/41237320
Email or phone of affected tester (no customers): gatantm+681@gmail.com
Issue reported by: Applause Internal Team
Bug source: Exploratory - Significant User Experience Deterioration
Device used: iPhone 12 mini, iOS 16.7
App Component: Workspace Settings

Action Performed:

  1. Launch new expensify
  2. Tap on Workspaces and open a workspace with tags enabled
  3. Tap on Tags
  4. Tap On more and Import spreadsheet
  5. Tap on Choose File and select the attached file
  6. Tap on Import

Expected Result:

App is back to the tags list with imported tags available

Actual Result:

The app crashes

Workaround:

Unknown

Platforms:

  • Android: App
  • Android: mWeb Chrome
  • iOS: App
  • iOS: mWeb Safari
  • iOS: mWeb Chrome
  • Windows: Chrome
  • MacOS: Chrome Safari

Screenshots/Videos

Bug7250654_1788480484390!Expensify-2026-09-04-024934.txt

MultiLevelTags-2026-09-04 01_56_12.454.csv

Bug7250654_1788480484395.RPReplay_Final1788478887.mp4

View all open jobs on GitHub

Applause Internal Information
Exported by: Mita Rachim
Bug ID: 7250654

Issue OwnerCurrent Issue Owner: @eh2077

Activity

  1. added
    BugSomething is broken. Auto assigns a BugZero manager.
    on Sep 24, 2026
  2. MelvinBot commented on Sep 24, 2026

    @MelvinBot
    Contributor

    Proposal

    TL;DR: This is not a tags bug. It is the known iOS native blob crash [Due for payment 2026-09-28] [Sentry: APP-KDN] iOS HybridApp native crash — RCTBlobManager slices a blob past its data length, already fixed by Fix APP-KDN: preserve NUL bytes in iOS TurboModule strings and clamp blob ranges, deployed to production in 9.4.90-2 on 2026-09-21. This report is from 9.4.71-0. Please retest on 9.4.90-2 or later — I expect it no longer crashes.

    What is the root cause of that problem?

    The app aborts on an uncaught Objective-C NSRangeException from -[NSData subdataWithRange:] inside RCTBlobManager, on the com.meta.react.turbomodulemanager.queue background queue. Because the method is void/async, React Native re-throws the exception out of the dispatch block, so no JS frame can catch it and the process calls abort().

    Multi-level tags is the only spreadsheet importer that re-uploads the raw file instead of posting parsed JSON, so it is the only import flow that builds a blob:

    The slice overruns because the JS-side blob descriptor records more bytes than native actually stored. JS counts the string's full UTF-8 byte length, while the NSString built from the JSI string is shorter — so subdataWithRange:{0, size} reads past the end.

    Why this particular file triggers it: the attached CSV is 327 bytes, contains zero NUL bytes, and starts with two back-to-back UTF-8 BOMs (plus a third inside the quoted "Department GL" header):

    00000000  ef bb bf ef bb bf 44 65  70 61 72 74 6d 65 6e 74  |......Department|
    00000010  2c 22 ef bb bf 44 65 70  61 72 74 6d 65 6e 74 20  |,"...Department |
    

    Foundation's UTF-8 decoder drops a leading BOM, giving native 324 bytes against a JS descriptor of 327 — a 3-byte shortfall, matching the Sentry message shape (range {0, 321} exceeds data length 318). This matters for triage: the earlier APP-KDN work attributed the desync to embedded NUL bytes, which this file does not have. The BOM is a second, distinct producer of the same desync.

    What changes do you think we should make in order to solve the problem?

    No new App code change is needed for the crash. Patch react-native+0.86.0+043+clamp-blob-range-APP-KDN.patch clamps the range instead of raising, so it covers the BOM case as well as the NUL case:

    Clamp the range to what is really here instead of letting subdataWithRange: raise an NSRangeException, which is fatal on this background queue because no JS frame can catch it.

    Recommended next steps:

    1. Retest this exact CSV on iOS with 9.4.90-2 or later. If it no longer crashes, close this as a duplicate of [Due for payment 2026-09-28] [Sentry: APP-KDN] iOS HybridApp native crash — RCTBlobManager slices a blob past its data length.
    2. During that retest, check the imported tag names. Nothing in this flow strips the BOM — the CSV goes to the backend byte-for-byte — so the created tag lists may be named with an invisible U+FEFF prefix. That would be a separate, cosmetic follow-up.
    Supporting evidence

    Crash report (Bug7250654_…Expensify-2026-09-04-024934.txt): EXC_CRASH / SIGABRT, abort() called, faulting queue com.meta.react.turbomodulemanager.queue.

    lastExceptionBacktrace, innermost first, with image indexes resolved (12 = React, 18 = CoreFoundation, 24 = Foundation, 31 = libobjc):

    CoreFoundation  __exceptionPreprocess + 164
    libobjc         objc_exception_throw + 60
    Foundation      +0x50494          (symbolicated as -[NSConcreteData copyWithZone:]+0)
    React           +0x2E5D4C         -[RCTBlobManager resolve:offset:size:]
    React           +0x2E5C34         -[RCTBlobManager resolve:]
    React           +0x2E6998         -[RCTBlobManager createFromParts:withId:]
    CoreFoundation  __invoking___ + 148
    Foundation      -[NSInvocation invoke] + 428
    Foundation      -[NSInvocation invokeWithTarget:] + 64
    React           +0x3151FC / +0x31A16C   ObjCTurboModule::performVoidMethodInvocation
    libdispatch     _dispatch_call_block_and_release …
    

    Two independent checks support the RCTBlobManager mapping:

    The -[NSConcreteData copyWithZone:]+0 symbol is a mis-symbolication: it is an adjacent NSData concrete-cluster method in Foundation's __TEXT, and symbolLocation: 0 indicates the symbolicator used a mismatched Foundation build for iOS 16.7 (20H19).

    Byte verification of the attached CSV: 327 bytes total; stripping NUL bytes leaves 327 bytes, so there are none. Three EF BB BF sequences, two at offset 0.

    Timeline: crash captured 2026-09-04 on 9.4.71-0 → PR #101062 merged 2026-09-18 → deployed to production in 9.4.90-2 on 2026-09-21. The reported build predates the fix.

    Alternatives considered and ruled out:

    • A JS-level error in the tags import. Ruled out — the crash is a native SIGABRT with no JS frames, and the App-side spreadsheet code (ImportSpreadsheet.ts, ImportSpreadsheetColumns.tsx) uses .at() with ?? '' fallbacks throughout, with no non-null assertions.
    • A third-party native module (document picker, blob-util, nitro-sqlite). Ruled out — the Podfile links those statically into the Expensify binary (image index 0), but the crashing frames are in React.framework (index 12). The nitro-thread-4 thread sitting in sqlite3_step is unrelated background Onyx I/O.
    • NSInvalidArgumentException from NSJSONSerialization or base64 decoding. Ruled out by the symbolicated APP-KDN stack: the raiser is subdataWithRange: with a range-exceeds-length message.
    • A legacy (non-TurboModule) bridged module. Ruled out — the new architecture is enabled (ios/Podfile:95, RCT_NEW_ARCH_ENABLED=1) and every ObjC TurboModule is invoked via NSInvocation, so that frame pattern is expected for RCTBlobManager, which is a real TurboModule.

    Not verified: I did not reproduce on an iOS device. The BOM mechanism specifically — that Foundation drops a leading BOM during the JSI-string-to-NSString conversion — is inferred from the byte counts and has not been confirmed on device. The crash identification itself does not depend on it: the clamp in patch 043 prevents the abort regardless of which producer created the size mismatch.

    What alternative solutions did you explore? (Optional)

    If the retest on 9.4.90-2+ still crashes, the next step is to strip the BOM before the file is read back for upload, using the existing removeInvisibleCharacters helper, and to reconcile the JS blob size with what native actually stored rather than trusting the JS-computed byte count. I would not pursue either until the retest proves the shipped clamp is insufficient.


    Next Steps for Contributor+ team:
    To accept: @MelvinBot implement [this](https://github.com/Expensify/App/issues/100367) to create a draft PR.
    To refine: @MelvinBot <your feedback>
    To reject: Explain why you are rejecting Melvin's proposal.


    view run

  3. added
    ExternalAdded to denote the issue can be worked on by a contributor
    on Sep 24, 2026
  4. melvin-bot commented on Sep 24, 2026

    @melvin-bot

    Triggered auto assignment to Contributor-plus team member for initial proposal review - @eh2077 (External)

  5. melvin-bot commented on Sep 24, 2026

    @melvin-bot

    Unable to auto-create job on Upwork. The BZ team member should create it manually for this issue.

  6. melvin-bot commented on Sep 29, 2026

    @melvin-bot

    @eh2077 Huh... This is 4 days overdue. Who can take care of this?

  7. eh2077 commented on Oct 1, 2026

    @eh2077
    Contributor

    checking

  8. melvin-bot commented on Oct 2, 2026

    @melvin-bot

    @eh2077 this issue is now 4 weeks old, please consider:

    • Finding a contributor to fix the bug
    • Closing the issue if BZ has been unable to add the issue to a VIP or Wave project
    • If you have any questions, don't hesitate to start a discussion in #expensify-open-source

    Thanks!

  9. melvin-bot commented on Oct 6, 2026

    @melvin-bot

    @eh2077 Huh... This is 4 days overdue. Who can take care of this?

  10. eh2077 commented on Oct 8, 2026

    @eh2077
    Contributor

    I can't reproduce the issue. @MelvinBot please help check again on recent fixes to see if they're relevant issues.

  11. MelvinBot commented on Oct 8, 2026

    @MelvinBot
    Contributor

    Your result matches the fix. The crash was already fixed by Fix APP-KDN: preserve NUL bytes in iOS TurboModule strings and clamp blob ranges. It reached production in 9.4.90-2. The report came from 9.4.70-0, before that fix.

    I recommend you close this issue as a duplicate of [Due for payment 2026-09-28] [Sentry: APP-KDN] iOS HybridApp native crash — RCTBlobManager slices a blob past its data length, which is already closed.

    Recent fixes I checked
    PR Merged Relevant?
    Fix APP-KDN: preserve NUL bytes in iOS TurboModule strings and clamp blob ranges 2026-09-18 Yes, this is the fix. The crash log shows an iOS native SIGABRT in -[RCTBlobManager resolve:offset:size:]. Patch 043 now clamps the range instead of throwing (patch:18). The patch is still on main.
    Read the picked file name from the URI when the native picker returns no metadata 2026-09-16 Same iOS picker flow, but different symptom. It fixes an "Invalid file type" modal, not a crash.
    Support GL codes for dependent multi-level tags 2026-09-25 No. It changes GL code editing after import, not the import upload path.
    Upgrade xlsx to 0.20.3 to fix security vulnerabilities 2026-10-06 No. The attached file is a CSV, and the multi-level tag import uploads the raw file.

    Other recent tag and spreadsheet PRs, such as category import mapping and the PolicyUtils refactor, don't touch the multi-level tag upload or the iOS blob code.

    Not verified: I didn't test on an iOS device. This crash is iOS-native only, so a web test can't confirm it. Your failed repro on a current build is the best evidence that the fix works.


    view run

  12. melvin-bot commented on Oct 13, 2026

    @melvin-bot

    @eh2077 Huh... This is 4 days overdue. Who can take care of this?

  13. eh2077 commented on Oct 14, 2026

    @eh2077
    Contributor

    I can't reproduce

    Screen.Recording.2026-10-14.at.10.42.21.PM.mov
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

BugSomething is broken. Auto assigns a BugZero manager.DailyKSv2ExternalAdded to denote the issue can be worked on by a contributorNot a priority

Type

No type

Projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions