Skip to content

LF-5381 Field Details page crashes when crop is added by another user while session is active#4252

Merged
kathyavini merged 4 commits into
integrationfrom
LF-5381-field-details-page-crashes-when-crop-is-added-by-another-user-while-session-is-active
Jul 23, 2026
Merged

LF-5381 Field Details page crashes when crop is added by another user while session is active#4252
kathyavini merged 4 commits into
integrationfrom
LF-5381-field-details-page-crashes-when-crop-is-added-by-another-user-while-session-is-active

Conversation

@kathyavini

Copy link
Copy Markdown
Collaborator

Description

World class AI vs taskSlice.js 😉

I have not tested it yet and it is not urgent; the crashes in this sequence of actions (as in testing below) predated our last major release.

AI's description below:


Fixes a family of TypeError crashes in the task selectors that occur when the Redux store holds tasks whose management plan / planting management plan entities are not (yet) in the store. Observed crash sites include the location details tabs (via the Map), the tasks list, and the Crop Catalogue:

  • taskSlice.jsCannot set properties of undefined (setting 'planting_management_plan') in getManagementPlanByPlantingManagementPlan (Immer produce over an undefined management plan)
  • taskSlice.jsCannot read properties of undefined (reading 'location_id') in the plant/transplant branch of taskEntitiesSelector
  • plantTaskSlice.jsCannot destructure property 'management_plan_id' of 'plantTask.planting_management_plan' as it is undefined (Crop Catalogue); transplantTaskSlice.js has the identical latent pattern

Root cause

taskEntitiesSelector and the by-management-plan-id selectors join tasks against managementPlanSlice and plantingManagementPlanSlice without guards, assuming every referenced plan entity is loaded. That assumption cannot hold:

  • Tasks and plans arrive from independent fetches. GET /task/{farm_id} does not embed planting_management_plan in plant_task/transplant_task (it never has — the upsert in getPlantingTasksAndPlantingManagementPlansSuccessSaga receives an empty list from this endpoint), and it never populates managementPlanSlice at all. Any tasks fetch that is not preceded by a plans fetch can therefore store tasks with dangling plan references — e.g. when another user on the same farm has created a management plan since the last plans fetch.
  • Redux Persist replays such an inconsistent snapshot across reloads, so the crash survives refresh until a healing plans fetch completes before a task selector evaluates.

The crashes were reproduced with two users on one farm and confirmed in the debugger: at the moment of the throw, the crashing task referenced a planting_management_plan_id absent from plantingManagementPlanEntities. The bug predates the locations refactor release (all three ingredients date from 2021-2022; verified empirically on the pre-release production commit).

Fix

Make the task selector joins total over partial state — one rule at every cross-slice join: a task that references an entity not yet in the store joins nothing (or is dropped) and reappears when the entity loads.

  • getManagementPlanByPlantingManagementPlan returns undefined when the management plan record is missing; both call sites filter these out
  • A task whose taskType is not in the store is dropped from taskEntitiesSelector output (every consumer dereferences taskType unconditionally)
  • The plant/transplant branch tolerates a missing subtask or missing planting management plan (joins [])
  • The assignee join uses optional chaining on userFarmEntities[farm_id]
  • plantTasksByManagementPlanIdEntitiesSelector / transplantTasksByManagementPlanIdEntitiesSelector skip tasks whose plan is unresolved

Behaviour consequence: a task whose plan entities have not arrived yet is temporarily excluded from task lists instead of crashing the page; it appears as soon as the plans fetch lands — the same end state the paired fetch always produced, without the crash window.

Out of scope (possible follow-up): embedding planting_management_plan in the GET /task/{farm_id} graph to shrink the window in which a task is unresolvable.

Jira link: https://lite-farm.atlassian.net/browse/LF-5381

Type of change

  • Bug fix (non-breaking change which fixes an issue)

How Has This Been Tested?

Two users on the same farm:

  1. User B logs in on a second browser; user A stays logged in.
  2. User B creates a crop management plan on a field.
  3. User A opens the field from the Map (details/tasks tabs), the tasks list, and the Crop Catalogue.
  4. Before the fix: TypeError error page, persisting across reloads. After the fix: pages render; the new plan's tasks appear once the page's own fetches complete.

Also regression-checked the fully-loaded case: tasks list, task read-only view, and Crop Catalogue with all data fetched.

Checklist:

  • I have commented my code, particularly in hard-to-understand areas
  • My changes generate no new warnings
  • I have added tests that prove my fix is effective or that my feature works
  • New and existing unit tests pass locally with my changes
  • The precommit and linting ran successfully
  • I have added or updated language tags for text that's part of the UI
  • I have ordered translation keys alphabetically (optional: run pnpm i18n to help with this)
  • I have added the GNU General Public License to all new files

…tore

Tasks and management plans arrive from independent fetches, and the tasks
response embeds no planting management plans, so any tasks fetch that is not
paired with a plans fetch stores tasks whose plan entities are absent;
redux-persist then replays such snapshots across reloads. Make the task
selector joins total over partial state: drop unresolvable plan references,
skip tasks with no cached task type, and skip plant/transplant tasks whose
planting management plan has not loaded.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@kathyavini kathyavini self-assigned this Jul 6, 2026
@kathyavini
kathyavini requested review from a team as code owners July 6, 2026 19:19
@kathyavini
kathyavini requested review from SayakaOno and removed request for a team July 6, 2026 19:19
@kathyavini kathyavini added the bug Something isn't working label Jul 6, 2026
@kathyavini kathyavini removed their assignment Jul 6, 2026
@kathyavini kathyavini changed the title LF-5381 Guard task selectors against plan entities missing from the s… LF-5381 Field Details page crashes when crop is added by another user while session is active Jul 20, 2026
kathyavini and others added 3 commits July 20, 2026 15:48
…en-crop-is-added-by-another-user-while-session-is-active
plantTask.planting_management_plan is populated from the plantingManagementPlan
slice, so it is the planting management plan that may be absent, not the
management plan. Name it in the comment.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@kathyavini kathyavini self-assigned this Jul 23, 2026

@kathyavini kathyavini left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

OK, I think it's mergeable.

I've tested it behaviourally in app as follows:

  • the original crash on locations > crops --> now the crop tile simply doesn't render until the fetch is re-triggered ✅
  • tasks won't render until the task type request completes ✅ --> this one I'm not as sure had any app relevance but it's certainly not dangerous

And the logic of the refactor -- running through taskSlice to see what was being built on a conjunction of separate API requests makes total sense. Probably should have been done earlier 😂

@kathyavini
kathyavini added this pull request to the merge queue Jul 23, 2026
Merged via the queue into integration with commit 7cf60c4 Jul 23, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant