Skip to content

Agents - Blank rows appear in agents table after deleting agents (stale local Onyx entries) #96889

Closed

Description

@MelvinBot

Problem

Blank rows appear in the Settings > Agents table after deleting agents. Reported by shawnborton on #95991 (comment) — blank rows showed on a branch build but not on staging, after previously deleting a bunch of agents.

blank agent rows

Root cause

The agents table renders one row per sharedNVP_agentPrompt_<accountID> collection key whose matching personalDetailsList[accountID] still exists (AgentsPage.tsx:82-107). A row is only skipped when personal details are missing entirely; if they exist but have an empty displayName/login, the row renders blank.

deleteAgent clears both the prompt key and the personal-details entry on the DELETE_AGENT success response (Agent.ts:289-309), but OPEN_AGENTS_PAGE only merges in what the server returns and never prunes local keys (Agent.ts:23-32). So any orphaned sharedNVP_agentPrompt_* entries left behind by earlier deletes (e.g. deleted offline, or from a build predating current cleanup logic) linger in local Onyx and render as blank rows. Staging looked clean because it had a different/fresher dataset.

Expected behavior

Deleted agents should never leave orphaned sharedNVP_agentPrompt_* entries behind, and the agents table should not render a row for an agent that has no valid personal details (empty name/login).

Reproduction

  1. Create several agents in Settings > Agents.
  2. Delete them (ideally reproduce with a delete that doesn't cleanly land the success response, e.g. offline then reconnect).
  3. Reopen Settings > Agents.
  4. Observe blank rows in the table.

Possible fixes to explore

  • Have OPEN_AGENTS_PAGE return the full agent set and reset the sharedNVP_agentPrompt collection so stale local keys are pruned, or
  • Guard the row builder in AgentsPage.tsx to skip rows whose personal details have no displayName and no login.

Surfaced from #95991 (comment)

Issue OwnerCurrent Issue Owner: @NicolasBonet

Activity

  1. NicolasBonet commented on Jul 23, 2026

    @NicolasBonet
    Contributor

    @MelvinBot let's explore the idea of making OPEN_AGENTS_PAGE reset the proper content, let's create a PR for that and let me know about any important consideration we might need to have.

  2. MelvinBot commented on Jul 23, 2026

    @MelvinBot
    ContributorAuthor

    Draft PR up: #96895

    What it does

    Makes openAgentsPage() reconcile local Onyx to the server's set instead of only merging on top of it. Before firing OPEN_AGENTS_PAGE it clears the existing non-pending sharedNVP_agentPrompt_* keys optimistically, so when the response merges the current agents back in, any key the server doesn't return is pruned — killing the orphaned blank rows. Added unit tests in AgentActionTest.ts covering the clear, the pending-skip, and the offline no-op. Typecheck, lint, and all Agent unit/UI tests pass.

    Important considerations

    1. This only works if OPEN_AGENTS_PAGE returns the full current agent set. The whole approach is clear-then-repopulate, so if the backend ever returns a partial/delta response, we'd wipe real agents that just weren't re-sent. This is the one thing to confirm before merging — I can't verify the backend command's response shape from the App repo. If it's not guaranteed to be a full snapshot, we should not ship this frontend approach.

    2. The cleanest fix is actually backend-side and lives outside App. Rather than the client guessing which local keys are stale, OPEN_AGENTS_PAGE could return the collection with replace semantics (a SET_COLLECTION, or explicit nulls for deleted agents). That prunes authoritatively, needs no optimistic clear, and avoids consideration Add Expensify eslint #3 entirely — but it's an Auth/Web-Expensify change, so it can't be done in this repo.

    3. Brief reload flicker. Clearing optimistically and repopulating from the response means the table momentarily empties and refills on every page open (one network round-trip). Worth a quick look on a slow connection to decide if it's acceptable or needs a loading guard.

    4. Offline is intentionally a no-op. While offline the read can't repopulate, so we skip the reset to avoid emptying the list until reconnect. Trade-off: orphaned rows persist until the next online open — acceptable, since the whole point is they're already stale.

    5. In-flight ops are preserved. Keys with a pendingAction (optimistic add/update/delete not yet confirmed) are skipped, so we never drop local state the server hasn't acknowledged.

    6. Complementary defensive guard (from the issue's other option). Even with this, guarding the row builder in AgentsPage.tsx to skip rows with no displayName and no login is a cheap belt-and-suspenders that protects against any future source of orphaned/empty entries. Not included here to keep the PR focused, but easy to add if you'd like both.

    My recommendation: if the backend can own the reset (#2), prefer that. Otherwise this frontend approach is viable provided #1 holds, and I'd pair it with the row guard in #6.

  3. NicolasBonet commented on Jul 23, 2026

    @NicolasBonet
    Contributor

    Went with the backend fix instead of the frontend optimistic clear: https://github.com/Expensify/Auth/pull/23170

    OpenAgentsPage returns the full authoritative agent set but was sending it with mergecollection, which never removes keys the client already has — that's the whole bug. The Auth PR switches it to setcollection so the client replaces the entire sharedNVP_agentPrompt_ collection on every Agents page open, pruning orphaned keys. Existing stale state on users' devices self-heals on the next page open; no App change or migration needed. Closed the frontend draft PR #96895 accordingly.

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

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions