Skip to content

Bug: task modal's upload-policy chain queries getPolicy with an empty organizationId — useGovernancePolicy and 4 sibling hooks lack the 'skip' guard #3086

Description

@larryro

Problem

Since the v0.4.14 boundary guard landed (#3021, shipped in #3052), org-scoped queries reaching the server with organizationId: "" are rejected as structured ORG_ID_REQUIRED. The client is still sending them:

Uncaught RLSError: Organization id is required.
  • GlitchTip TALE-PROJECT-11R — born 2026-08-25T06:11Z, hours after v0.4.14 deployed, exactly as the fix(platform): classify dead-org errors and recover stale clients #3052 review predicted for the residual caller-side gap. 32 events by 2026-08-27T02:19Z and accelerating (10 by 8/25 12:42Z → 26 by 8/26 11:48Z → 32 now), from authenticated demo users in normal use — not stale tabs.
  • All 32 events carry func: governance/queries:getPolicy; no other org-scoped function has ever fired with an empty id.
  • Pre-guard predecessor: TALE-PROJECT-OU — Not a member of organization <empty>, 92 events 2026-07-07 → 2026-08-25, quiet since the guard went live. Its sampled events were the same getPolicy path (40/42).

Root cause (verified at 0453e65)

Components mount org-scoped subscriptions while their parent record is still resolving, substituting '' for the org id. The one chain matching every live event:

  1. task-modal.tsx#L1026-L1029 — useConvexFileUpload({ organizationId: task?.organizationId ?? '', … }) mounts while useTask(taskId) is still resolving, so task is undefined
  2. use-convex-file-upload.ts#L104 — useUploadPolicy(config.organizationId) unconditionally
  3. governance/hooks/queries.ts#L253-L254 — useUploadPolicy → useGovernancePolicy(organizationId, 'upload_policy')
  4. governance/hooks/queries.ts#L52-L59 — useGovernancePolicy subscribes with no 'skip' guard, so the '' goes over the wire.

The same file already has the house pattern next door: usePasswordPolicy (#L100-L108) passes 'skip' until the org id is truthy — as do useAccessibleModels, useLegalHolds, useOrgMembersForPicker.

Where else (same class)

Unguarded siblings in the same file — will fire for any falsy caller: usePiiConfig (L45), useDsarPolicyForUi (L62), useMyFeatureFlags (L68), useMyBudgetStatus (L74).

Known ?? '' feeders (zero server events to date, but the same race): task-modal.tsx L984 useActorDirectory, L996 useTaskStatusChoreography, L1000 useTaskSubjectContract; kanban-board.tsx L55 and use-task-board-dnd.ts L92 (tasks[0]?.organizationId ?? ''); message-toolbar.tsx L89 (useFeedbackActions); dictation-button.tsx L82; members-settings.tsx L68.

Suggested

  1. 'skip'-on-falsy guard in useGovernancePolicy and the four unguarded siblings — the smallest change that silences the live stream, and it covers every current and future caller of those hooks.
  2. Sweep the ?? '' call sites so org-scoped queries pass 'skip' (or don't mount) until a real id exists.
  3. Close the class: dev-assert (or auto-skip) in useConvexQuery when an organizationId arg is '', or type org ids so ?? '' no longer typechecks into query args.

Acceptance

  • Opening/closing the task modal (including the E2E project specs) produces no new ORG_ID_REQUIRED events.
  • TALE-PROJECT-11R stays flat after the next demo deploy; no '' org id leaves the client.

Tracking

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions