You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Rules - Auto - Unable to import Merchant Rules offline #101411
changed the title [-]Rules-Auto- Unable to import Merchant Rules offline[/-][+]Rules - Auto - Unable to import Merchant Rules offline[/+]on Sep 17, 2026
The disabled Import button is intentional — merchant rules can't be created offline because the import needs a non-retryable API call. The actual bug is that merchant rules is the only import flow that lets you walk into the mapping page while offline, so it's the only one where you reach a silently greyed-out button with no explanation.
Every comparable flow blocks navigation at the entry point with an explicit "You appear to be offline." modal. Merchant rules doesn't:
Once you're on the page, the button is correctly disabled but gives zero feedback: ImportSpreadsheetColumns.tsx:118 sets isDisabled on a plain Button — no tooltip, no inline message, no FormAlertWithSubmitButton. Tapping it or pressing Enter does nothing at all.
Supporting evidence
The offline gate is deliberate and correct.ImportedMerchantRulesPage.tsx:332 computes canImportOffline, true only when the import short-circuits entirely client-side (every row skipped as a duplicate/unknown category). It's passed through as shouldDisableButtonWhenOffline={!canImportOffline}. In the QA repro the CSV produces net-new rules, so canImportOffline is false and the button is disabled — working as designed.
Not a merchant-rules regression.shouldDisableButtonWhenOffline defaults to true (ImportSpreadsheetColumns.tsx:56) and grep -rn shouldDisableButtonWhenOffline src/ matches only this page and the component itself. All seven other consumers (categories, tags, multi-level tags, members, per-diem, company cards, wallet transactions) inherit true and are also disabled offline. Keep merchant rules import button active offline for the all-skipped path only relaxed the previously unconditional isDisabled={isOffline}.
The only offline feedback on screen is the generic You appear to be offline. banner from ScreenWrapper, rendered below the footer button and identical on every screen — it never connects itself to the Import button.
Alternative explanations ruled out: not a validation failure (validation only runs on submit and the QA mapping satisfies hasMerchantFilterColumn + hasActionColumn); not a loading/parse failure (the page rendered the mapping UI, so the spreadsheet parsed); not an unloaded-categories problem (areCategoriesReady only narrowscanImportOffline, it can't disable an already-enabled button).
What changes do you think we should make in order to solve the problem?
Add an offline guard to the merchant-rules import entry point in getImportMerchantRulesOption.ts, matching what categories/tags/members/per-diem already do: when isOffline, show the common.youAppearToBeOffline / common.thisFeatureRequiresInternet confirm modal and don't navigate. This requires threading isOffline and a showOfflineModal callback in from PolicyRulesPageRevamp.tsx, the same way showReadOnlyModal is already passed.
The offline user then gets a clear reason instead of a dead button, and the flow behaves like every other spreadsheet import.
What alternative solutions did you explore? (Optional)
Surface an inline offline message next to the disabled button in ImportSpreadsheetColumns (switch to FormAlertWithSubmitButton with an offline message, or add a tooltip). This fixes the silent-disabled UX for all eight import flows at once, not just merchant rules. Better long-term, larger blast radius — worth doing in addition if we want consistency.
Do nothing / close as by-design. Defensible on strict behavior grounds, but the issue's expected result is "the offline import state is handled properly" and an unexplained dead button isn't that.
Next Steps for Contributor+ team: To accept:@MelvinBot implement [this](https://github.com/Expensify/App/issues/101411) to create a draft PR. To refine:@MelvinBot <your feedback> To reject: Explain why you are rejecting Melvin's proposal.
@truph01 this issue was created 2 weeks ago. Are we close to approving a proposal? If not, what's blocking us from getting this issue assigned? Don't hesitate to create a thread in #expensify-open-source to align faster in real time. Thanks!
Activity
applause-bot commentedon Sep 17, 2026
While Applause doesn't embed attachments for this bug, here are the links for quick view:
https://platform.applause.com/services/links/v1/external/d804ded318574cdab804bdc97d69ee02bba061f19f7ecab2b31c395636199de1
[-]Rules-Auto- Unable to import Merchant Rules offline[/-][+]Rules - Auto - Unable to import Merchant Rules offline[/+]MelvinBot commentedon Sep 17, 2026
Proposal
What is the root cause of that problem?
The disabled
Importbutton is intentional — merchant rules can't be created offline because the import needs a non-retryable API call. The actual bug is that merchant rules is the only import flow that lets you walk into the mapping page while offline, so it's the only one where you reach a silently greyed-out button with no explanation.Every comparable flow blocks navigation at the entry point with an explicit "You appear to be offline." modal. Merchant rules doesn't:
src/pages/workspace/rules/getImportMerchantRulesOption.ts:47-56— checkscanWriteRulesand upgrade, but never checksisOffline.src/pages/workspace/categories/WorkspaceCategoriesPage.tsx:352-355—if (isOffline) { showOfflineModal(); return; }. Tags, members, and per-diem all do the same.Once you're on the page, the button is correctly disabled but gives zero feedback:
ImportSpreadsheetColumns.tsx:118setsisDisabledon a plainButton— no tooltip, no inline message, noFormAlertWithSubmitButton. Tapping it or pressing Enter does nothing at all.Supporting evidence
The offline gate is deliberate and correct.
ImportedMerchantRulesPage.tsx:332computescanImportOffline, true only when the import short-circuits entirely client-side (every row skipped as a duplicate/unknown category). It's passed through asshouldDisableButtonWhenOffline={!canImportOffline}. In the QA repro the CSV produces net-new rules, socanImportOfflineis false and the button is disabled — working as designed.Not a merchant-rules regression.
shouldDisableButtonWhenOfflinedefaults totrue(ImportSpreadsheetColumns.tsx:56) andgrep -rn shouldDisableButtonWhenOffline src/matches only this page and the component itself. All seven other consumers (categories, tags, multi-level tags, members, per-diem, company cards, wallet transactions) inherittrueand are also disabled offline. Keep merchant rules import button active offline for the all-skipped path only relaxed the previously unconditionalisDisabled={isOffline}.The only offline feedback on screen is the generic
You appear to be offline.banner fromScreenWrapper, rendered below the footer button and identical on every screen — it never connects itself to the Import button.Alternative explanations ruled out: not a validation failure (validation only runs on submit and the QA mapping satisfies
hasMerchantFilterColumn+hasActionColumn); not a loading/parse failure (the page rendered the mapping UI, so the spreadsheet parsed); not an unloaded-categories problem (areCategoriesReadyonly narrowscanImportOffline, it can't disable an already-enabled button).What changes do you think we should make in order to solve the problem?
Add an offline guard to the merchant-rules import entry point in
getImportMerchantRulesOption.ts, matching what categories/tags/members/per-diem already do: whenisOffline, show thecommon.youAppearToBeOffline/common.thisFeatureRequiresInternetconfirm modal and don't navigate. This requires threadingisOfflineand ashowOfflineModalcallback in fromPolicyRulesPageRevamp.tsx, the same wayshowReadOnlyModalis already passed.The offline user then gets a clear reason instead of a dead button, and the flow behaves like every other spreadsheet import.
What alternative solutions did you explore? (Optional)
ImportSpreadsheetColumns(switch toFormAlertWithSubmitButtonwith an offline message, or add a tooltip). This fixes the silent-disabled UX for all eight import flows at once, not just merchant rules. Better long-term, larger blast radius — worth doing in addition if we want consistency.WorkspaceCompanyCardsSettingsPage.tsx:200-215has the same unguarded entry point and would need the same treatment.Possibly related
Next Steps for Contributor+ team:
To accept:
@MelvinBot implement [this](https://github.com/Expensify/App/issues/101411)to create a draft PR.To refine:
@MelvinBot <your feedback>To reject: Explain why you are rejecting Melvin's proposal.
view run
melvin-bot commentedon Sep 17, 2026
Triggered auto assignment to Contributor-plus team member for initial proposal review - @truph01 (
External)melvin-bot commentedon Sep 17, 2026
Unable to auto-create job on Upwork. The BZ team member should create it manually for this issue.
truph01 commentedon Sep 18, 2026
@jponikarchuk Could you give me more detail about this expected behavior?
Currently, the CTA button is disabled, I think it is fine.
jponikarchuk commentedon Sep 18, 2026
@truph01 We have such steps in the TC
melvin-bot commentedon Sep 21, 2026
@truph01 Whoops! This issue is 2 days overdue. Let's get this updated quick!
melvin-bot commentedon Sep 23, 2026
@truph01 Eep! 4 days overdue now. Issues have feelings too...
3 remaining items
melvin-bot commentedon Oct 1, 2026
@truph01 this issue was created 2 weeks ago. Are we close to approving a proposal? If not, what's blocking us from getting this issue assigned? Don't hesitate to create a thread in #expensify-open-source to align faster in real time. Thanks!
melvin-bot commentedon Oct 5, 2026
@truph01 Eep! 4 days overdue now. Issues have feelings too...
truph01 commentedon Oct 7, 2026
@jponikarchuk Do you have any feedback on this comment? Are we good to close this issue?
melvin-bot commentedon Oct 12, 2026
@truph01 Huh... This is 4 days overdue. Who can take care of this?
melvin-bot commentedon Oct 14, 2026
@truph01 Still overdue 6 days?! Let's take care of this!
melvin-bot commentedon Oct 15, 2026
@truph01 this issue is now 4 weeks old, please consider:
Thanks!
melvin-bot commentedon Oct 16, 2026
@truph01 Now this issue is 8 days overdue. Are you sure this should be a Daily? Feel free to change it!
truph01 commentedon Oct 17, 2026
Closed since it is not a bug