Skip to content

finding(app-shell): onManageChanged reads a failed package-list refresh as "the package was deleted" and evicts the author to /home #7821

Description

@claude

Observation

Found while fixing objectui#7368 (the PackageSwitcher fetch error posture). Deliberately
not fixed there: that card is about the switcher's silence, this one is about a wrong
decision
the same silence hides, and the correct behaviour needs a ruling rather than a
one-line toast.

onManageChanged in packages/app-shell/src/views/studio-design/StudioDesignSurface.tsx
(:340-354 on origin/main 900f8d995) — the callback the PackageDetailSheet fires after any
package lifecycle action (disable / duplicate / delete / publish / manifest edit):

const onManageChanged = React.useCallback(async () => {
  let list: PkgEntry[] = [];
  try {
    list = await fetchPackages();
    setPkgs(list);
  } catch {
    /* keep the stale list */
  }
  const managedId = manage?.manifest.id;
  if (!managedId) return;
  if (!list.some((p) => p.id === managedId)) {
    // Deleted — only navigate away if it was the package we're editing.
    if (managedId === packageId) {
      const next = list[0];
      navigate(next ? `/studio/${encodeURIComponent(next.id)}/${tab}` : '/home');
    }
    return;
  }
  ...

The catch comment says "keep the stale list". That is true of the pkgs state — which is
simply not written — but not of the local list, which was initialised to [] and stays
[]. Three lines later !list.some(...) is therefore always true, so a failed refresh takes
the branch labelled // Deleted. If the managed package is the one under the editor, the surface
navigates away: list[0] is undefined, so the destination is /home.

Net effect: a transient failure of GET /api/v1/packages — a 503 from the durable half, a
network blip, an auth expiry — immediately after any package lifecycle action reads as "your
package was deleted"
and silently evicts the author from the Studio editor to the home page.
No toast, no confirmation, and the package is still there.

The .catch is the same shape objectui#7368 is about, and this is the fourth call site named in
that card's triage. The other three are handled in the PR for #7368; this one is held back
because a toast alone would leave the eviction firing.

Worth deciding

  1. Skip the eviction check when the refresh failed — treat "I could not find out" as "no
    information", not as "gone". Cheapest, and matches the sibling catch two blocks down
    (/* keep the current snapshot */), which already declines to act on a failure.
  2. Re-derive from the state list rather than the local [], so a failed refresh falls back
    to what the switcher last knew.
  3. Report the refresh failure as well (app-shell(studio): PackageSwitcher swallows the package-list fetch failure, leaving the raw package id in the top bar #7368's posture), independently of 1/2.

Re-check

grep -n "onManageChanged" -A 20 packages/app-shell/src/views/studio-design/StudioDesignSurface.tsx

Related, not a duplicate: objectui#7373 is about the destination of recovery redirects
(hard-coded /home vs the declared landing page). This card is about the redirect firing at all,
on a false premise.


Generated by Claude Code

Activity

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