Skip to content

MetadataProvider's first-boot seed entry is written under the no-org scope, so the entry the next boot looks for is never there #5243

Description

@os-support-ai

Observation, filed unassigned while implementing #5198 (PR #5242). Pre-existing on main, untouched by that PR, and not a disclosure — the orphaned entry belongs to the same user who wrote it. Filing it because it silently costs the seed cache its whole benefit on a browser's first login, and because it is invisible: every symptom looks like a normal cache miss.

What I measured

While writing the counter-pin for #5198 ("a same-principal remount still seeds instantly"), the test read an empty app list until I stamped ActiveOrganizationStoragebefore the first render. Tracing why:

MetadataProvider derives its cache scope from ActiveOrganizationStorage.get() || NO_ORG_SCOPE (activeOrgScope, objectui#4486). On a browser that has never signed in before, that storage is empty when the console mounts, and AuthProvider only resolves the active organization aftergetSession returns, in refreshOrganizations. The eager app fetch starts at mount and typically lands first, so:

  1. first login: apps fetched, written under the no-org scope (objectui:metadata:app:@none:...), then AuthProvider stamps the real org id;
  2. every later boot: the key now resolves to the real org, the no-org entry is never looked at again, and the seed misses;
  3. the miss is repaired by the fetch that follows, which writes the correctly scoped entry — so it self-heals after one boot, and the stale no-org entry stays in sessionStorage until the tab closes.

Net cost: the seed optimization does nothing on the boot right after a first login, plus one orphaned entry per tab. Nothing renders wrong, which is exactly why nothing reports it.

primeCacheFor in MetadataProvider.orgScopedCache.test.tsx sets the org id before rendering, which models a returning browser correctly — so the existing pins never see this window, and neither do the ones I added (they follow the same modelling deliberately).

Adjacent, same window, worth a look at the same time

That first-login GET /api/v1/meta/* also goes out with noX-Tenant-ID header, since createAuthenticatedFetch reads the same empty storage. Whatever the server does with a missing tenant on an authenticated session is presumably fine, but the cached answer is then labelled @none while being whatever the server chose — worth confirming those two agree before deciding what the right key is.

Not attempted here

Out of scope for #5198 and deliberately left alone: the fix is a design choice (wait for the org before seeding? re-key the entry when the org resolves? treat the no-org entry as adoptable by the first org that resolves?), and each option trades a request against a render. No code was touched for this.

Metadata

Metadata

Assignees

Type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions