Skip to content

Prevent crashes when task types, users and related records are missing - #2163

Merged
frankrousseau merged 53 commits into
cgwire:mainfrom
NicoPennec:main
Jul 25, 2026
Merged

Prevent crashes when task types, users and related records are missing#2163
frankrousseau merged 53 commits into
cgwire:mainfrom
NicoPennec:main

Conversation

@NicoPennec

@NicoPennec NicoPennec commented Jul 24, 2026

Copy link
Copy Markdown
Member

Problem

  • The compact NDJSON with-tasks decoder could omit assignees, leaving task.assignees undefined and crashing TaskInfo.
  • isAssigned dereferenced a null user during logout/login teardown, and the Task page kept doing the same in its comment ownership checks.
  • Task types, persons, statuses, episodes, departments, asset types, sequences, and productions resolved from permission-filtered or partially loaded global maps were dereferenced without a guard against a miss. This crashed task links, task-type tags, comments, validation tags, at-mentions, the attachment modal, task, kanban and edits page titles, TaskInfo, list and comment CSV exports, breakdown filenames and filter queries, list and task-list sorting, ready-for tooltips, task filtering, quota, chat, budget and production stats, stats validation columns, task-type settings, the entity load paths, and playlist load.
  • Further sites of the same class remained unguarded: the expanded production-stats color bar sitting next to an already-guarded title, the remaining task-page task-type lookups, the sequences non-tvshow title, the all-tasks status stats, the entity preview and time-log task types, the estimation approval-late filter, the supervisor task index, budget labels and sorting, the my-checks episode options, the production schedule items, the metadata descriptor rename, the global search entity path, and the bulk todo edits.
  • Some of those guards only moved the crash: optional-chaining for_entity fed an empty required :type param to vue-router, so task links, validation tags and comment preview links still threw at resolution.
  • Sites of the same class sitting one line from a guarded one stayed exposed: the Task page user reads, the TaskInfo comment export author and status, four filter-modal query builders, the my-checks filter lists, the production stats task types, the quota and task-type lists, and the validation cell column shared by the five entity lists.
  • A person missing from the map left an undefined position key in the exported budget CSV and a NaN monthly salary in the budget totals.
  • The ESLint parser was pinned to ES2020 and rejected logical assignment.
  • Some npm dependencies are outdated.

Solution

  • Default assignees to an empty array in the compact task decoder.
  • Guard isAssigned and the comment ownership checks against a null user on both the task sidebar and the task page.
  • Guard every affected lookup with optional chaining, string fallbacks, early returns, or filter(Boolean), also fixing a pre-existing assignee sort that compared a name to a person object.
  • Extract the duplicated assignee sort into a sortByPersonName helper shared by the shot, asset, edit, episode and sequence stores, and cover it with a unit test.
  • Fall back to the task's own entity type so the :type segment always resolves, covered by unit tests on getTaskPath.
  • Guard the remaining reads, filtering unresolved records out of the lists that feed them so the holes never reach the template.
  • Default the budget person fields so the CSV export and the totals stay meaningful.
  • Raise the ESLint parser to ES2021.
  • Bump npm dependencies.

Lets source use ES2021 syntax such as the logical assignment operators. Parse-only: browser support is unchanged, still governed by browserslist and the Vite es2020 target.
Compact NDJSON with-tasks rows rebuild assignees positionally; a field layout without it left task.assignees undefined and crashed unguarded reads (TaskInfo isAssigned). Default it to [] at decode, and harden the two isAssigned reads.
user is null during logout teardown; optional-chain the user id read in isAssigned and the comment ownership checks.
taskTypeMap.get returns undefined for a stale task_type_id; return early instead of reading for_entity on it.
Optional-chaining for_entity only moved the crash: pluralizeEntityType falls
back to '' and vue-router rejects an empty required :type param, so the link
threw during resolution instead of rendering. The segment is never read back,
so getTaskRouteEntity falls back to the task's own entity type.
USER_LOGOUT sets state.user to null while the page is still mounted, so the
comment ownership checks and isAssigned threw during teardown. TaskInfo was
already fixed; this brings its sibling page to parity.
resolveAuthor returns undefined when the person is not in personMap, which is
why the template already reads comment.person?.id. The export row builder still
dereferenced the author and the status.
taskTypeList mapped validation columns through taskTypeMap without filtering,
so a hole crashed the four remaining query builders, addTaskTypeFilter and the
active watcher. Filtering at the source also keeps the assetsready clause the
user configured instead of silently dropping it.
The three reset lists pushed raw map lookups into arrays handed to sortByName,
which dereferences name and threw, next to the episode options this PR already
guarded.
The task type sort still dereferenced a missing task type, and the guarded
status reads rendered a sizeless-but-coloured segment titled "undefined".
Filtering both lists removes the crash and the placeholder segment.
Both computed lists mapped ids through taskTypeMap and dereferenced the result,
so they threw before the guards added elsewhere in those files could run.
The column prop already defaults to null and the ten list call sites pass a raw
taskTypeMap lookup, but cellStyle dereferenced its colour. Guarding here covers
every list instead of each header separately.
Keying on the task type made the key nullish for an unresolved column. The two
sibling stats lists already key on columnId.
Defaulting the person fields to undefined leaked a budget.positions.undefined
key into the exported CSV and turned the monthly salary into NaN through the
daily salary multiplication.
A task type present but without for_entity fell through the earlier guard and
still produced an empty :type, and reaching the episode branch by fallback with
no entity_id left episode_id empty instead. Both now keep a resolvable route.
@frankrousseau
frankrousseau merged commit ede3391 into cgwire:main Jul 25, 2026
5 checks passed
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