[GH-ISSUE #7677] CSV import preview shows false updates for unresolved categories #117538

Closed
opened 2026-06-11 12:46:14 -05:00 by GiteaMirror · 0 comments
Owner

Originally created by @tekumara on GitHub (May 2, 2026).
Original GitHub issue: https://github.com/actualbudget/actual/issues/7677

This was generated by AI during triage.

Description

The CSV import preview can show matched transactions as “updated” when the only difference is unresolved CSV category text.

This makes the preview hard to trust for large imports: users may see many matched transactions flagged for update even though final import would drop the unresolved category and apply no meaningful category change.

Steps to reproduce

  1. Have an existing transaction with the same date, amount, imported payee/payee details, and notes as a CSV row, but no category:

    {
      date: "2025-10-13",
      amount: 500000,
      imported_payee: "Example Payee",
      notes: "Example note",
      category: null
    }
    
  2. Import a CSV containing a matching row whose Category value does not exist as an Actual category:

    Date,Payee,Notes,Debit,Credit,Category,SubCategory
    13/10/2025,Example Payee,Example note,,5000.00,Deposits,Deposit
    
  3. In the CSV import UI, map the Category column to Actual’s category field.

  4. View the import preview.

Actual result

The transaction is shown as matched and updated / not ignored.

The apparent update is caused by unresolved raw category text being sent to reconciliation, for example:

category: "Deposits"

Expected result

The transaction should be shown as ignored/no-op because the unresolved category should be treated as null, matching final import behavior.

Suspected cause

Preview and final import appear to handle unresolved category mapping differently.

Preview appears to only replace the raw CSV category when resolution succeeds:

const category_id = parseCategoryFields(trans, categories);

if (category_id != null) {
  trans.category = category_id;
}

Final import appears to always assign the resolved value:

const category_id = parseCategoryFields(trans, categories);
trans.category = category_id;

So when category resolution fails, preview keeps raw CSV text while final import sets category to null.

Suggested fix

Normalize categories the same way for preview and final import. The preview path should probably assign the resolved category result even when it is null:

const category_id = parseCategoryFields(trans, categories);
trans.category = category_id;

Version

Observed against Actual 26.4.0.

Originally created by @tekumara on GitHub (May 2, 2026). Original GitHub issue: https://github.com/actualbudget/actual/issues/7677 > *This was generated by AI during triage.* ### Description The CSV import preview can show matched transactions as “updated” when the only difference is unresolved CSV category text. This makes the preview hard to trust for large imports: users may see many matched transactions flagged for update even though final import would drop the unresolved category and apply no meaningful category change. ### Steps to reproduce 1. Have an existing transaction with the same date, amount, imported payee/payee details, and notes as a CSV row, but no category: ```js { date: "2025-10-13", amount: 500000, imported_payee: "Example Payee", notes: "Example note", category: null } ``` 2. Import a CSV containing a matching row whose `Category` value does not exist as an Actual category: ```csv Date,Payee,Notes,Debit,Credit,Category,SubCategory 13/10/2025,Example Payee,Example note,,5000.00,Deposits,Deposit ``` 3. In the CSV import UI, map the `Category` column to Actual’s category field. 4. View the import preview. ### Actual result The transaction is shown as matched and updated / not ignored. The apparent update is caused by unresolved raw category text being sent to reconciliation, for example: ```js category: "Deposits" ``` ### Expected result The transaction should be shown as ignored/no-op because the unresolved category should be treated as `null`, matching final import behavior. ### Suspected cause Preview and final import appear to handle unresolved category mapping differently. Preview appears to only replace the raw CSV category when resolution succeeds: ```ts const category_id = parseCategoryFields(trans, categories); if (category_id != null) { trans.category = category_id; } ``` Final import appears to always assign the resolved value: ```ts const category_id = parseCategoryFields(trans, categories); trans.category = category_id; ``` So when category resolution fails, preview keeps raw CSV text while final import sets category to `null`. ### Suggested fix Normalize categories the same way for preview and final import. The preview path should probably assign the resolved category result even when it is `null`: ```ts const category_id = parseCategoryFields(trans, categories); trans.category = category_id; ``` ### Version Observed against Actual `26.4.0`.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/actual#117538