Skip to content

FIX: distinguish catalog-unavailable from unregistered in Initializers page - #2464

Open
fei (feiiiiii5) wants to merge 4 commits into
microsoft:mainfrom
feiiiiii5:fix/initializer-catalog-unavailable
Open

FIX: distinguish catalog-unavailable from unregistered in Initializers page#2464
fei (feiiiiii5) wants to merge 4 commits into
microsoft:mainfrom
feiiiiii5:fix/initializer-catalog-unavailable

Conversation

@feiiiiii5

@feiiiiii5 fei (feiiiiii5) commented Aug 22, 2026

Copy link
Copy Markdown
Contributor

Description

Fixes #2442

When /api/initializers/settings succeeds but the registered-initializer catalog request (GET /api/initializers) fails transiently, the Initializers page preserved configured baseline settings but described valid entries as Initializer is no longer registered. A temporary metadata availability failure was therefore presented as a definitive registration problem.

Changes:

  • initializerLookup.ts: exports CatalogStatus = 'loading' | 'loaded' | 'error'; findRegisteredInitializer resolves a settings entry to its catalog entry or undefined (no synthetic placeholder objects); initializerFallbackDescription reports Initializer is no longer registered. only when the catalog loaded successfully — loading and error both render Catalog metadata temporarily unavailable.
  • Initializers.tsx: tracks catalogStatus derived from the Promise.allSettled result instead of a boolean flag
  • BaselineInitializers.tsx / AdditionalInitializers.tsx: rows render env vars and parameter summaries only for real catalog entries; a catalog error shows an inline "editing disabled" note and disables Edit so an outage cannot null out saved parameters via a schema-less editor
  • AvailableInitializersDialog.tsx: an empty list during a catalog error reports the catalog as unavailable instead of No registered initializers were found

Behavior note: Edit now requires a real catalog entry, so a genuinely unregistered name can no longer open the schema-less editor either.

Tests and Documentation

  • initializerLookup.test.ts: unit tests for the new lookup and status-based fallback copy
  • AdditionalInitializers.test.tsx: Edit stays disabled while the catalog is unavailable; new case asserting Edit stays disabled for a name missing from a loaded catalog (no synthetic env vars / parameter summary rendered)
  • BaselineInitializers.test.tsx / AvailableInitializersDialog.test.tsx: updated for the status-based props
  • Full Initializers suite (78 tests), tsc --noEmit, and eslint pass
  • No documentation changes needed (UI-only behavioral fix)

This contribution was developed with LLM assistance following repo conventions.

initializer={resolveRegisteredInitializer(
item.initializer_name,
registeredInitializers,
catalogUnavailable,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When the catalog request fails, this placeholder has no parameter schema, but Edit remains enabled. Clicking Save then replaces existing parameters, such as { mode: 'strict' }, with null; please disable editing until the catalog reloads or preserve the existing parameters while the schema is unavailable.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in ddf816c (took the disable-until-reload option): Edit is disabled while catalogAvailable is false, the card states "Initializer catalog is unavailable; editing is disabled until it reloads." inline, and Apply/Remove stay enabled since they only operate on stored parameters — so a catalog outage can no longer null out existing parameters like {"mode": "strict"}. Covered by two new tests: Edit disabled + inline explanation while unavailable, and Apply/Remove remaining usable.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correction to the reply above: the referenced ddf816c was never pushed to the remote. The fix is on the branch as 5796799, and a follow-up in d291d77 additionally reports catalog unavailability in the available-initializers dialog. The PR-level comments have the details.

@romanlutz

Copy link
Copy Markdown
Contributor

You may need to push your fix commit :-)

@feiiiiii5

Copy link
Copy Markdown
Contributor Author

You were right — the commit referenced above never made it to the remote. Apologies for that. It is now pushed as 5796799 (re-done on top of the branch): Edit is disabled while catalogAvailable is false, the card states that editing resumes once the catalog loads, and a regression test covers the disabled state with the stored parameters still visible. Apply and Remove remain available since they use the stored parameters directly.

<BaselineInitializers
items={settings.baseline}
registeredInitializers={registeredInitializers}
catalogUnavailable={catalogUnavailable}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we pass this unavailable state to AvailableInitializersDialog as well, or disable its trigger when the catalog request fails? On an initial failure, the dialog receives an empty array and says "No registered initializers were found," which still presents a temporary outage as a definitive empty catalog.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in d291d77: AvailableInitializersDialog now receives catalogUnavailable and reports that the catalog cannot be shown until it loads, instead of "No registered initializers were found". The trigger stays enabled since the dialog is a read-only reference; the empty-registry message is unchanged for a successful fetch that registers nothing.

@feiiiiii5

Copy link
Copy Markdown
Contributor Author

Done in d291d77 — took the first option: AvailableInitializersDialog now receives catalogUnavailable and reports that the catalog cannot be shown until it loads, instead of "No registered initializers were found". I kept the trigger enabled rather than disabling it: the dialog is a read-only reference, and a stale-but-real list after a refresh failure stays usable. The empty-registry message is unchanged for a successful fetch that registers nothing. Regression test covers both messages.

@feiiiiii5

Copy link
Copy Markdown
Contributor Author

Follow-up: 20a5d7c adopts the status-union design suggested in review and removes the synthetic placeholder entries. (The suggestion arrived on #2469 — a duplicate PR I opened by mistake, now closed in favor of this one. Apologies for the noise.)

  • CatalogStatus = 'loading' | 'loaded' | 'error' replaces the boolean flag; findRegisteredInitializer returns RegisteredInitializer | undefined
  • Only a loaded catalog renders the definitive Initializer is no longer registered. fallback; loading and error both report Catalog metadata temporarily unavailable.
  • Rows render env vars / parameter summaries only for real catalog entries; the inline unavailability note and disabled Edit from the earlier review rounds are preserved
  • One behavior change to flag: Edit now requires a real catalog entry, so a genuinely unregistered name can no longer open the schema-less editor — this closes the same parameter-nulling hazard Roman Lutz (@romanlutz) identified, for the unregistered case

Full Initializers suite (78 tests), tsc --noEmit, and eslint all pass.

setRegisteredInitializers(registeredResult.value.items)
setCatalogStatus('loaded')
} else {
setCatalogStatus('error')

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If a refresh fails after a successful load, registeredInitializers keeps the old entries while catalogStatus becomes error. That leaves stale metadata visible and the Add controls enabled even though catalog-dependent UI should be unavailable; could we clear the array here or make consumers ignore it unless the status is loaded, with a regression test for success followed by refresh failure?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in 749e65e (merged main as acf7bb0 so this sits on current main).

Took the first option — the failure branch now drops the catalog instead of keeping the last good list:

} else {
  // Drop the previous catalog: entries left behind would keep rendering stale
  // descriptions and env vars, and would keep Add enabled off the first stale name.
  setRegisteredInitializers([])
  setCatalogStatus('error')

Both symptoms you named follow from that one change, with no new gating logic:

  • Stale metadata: every consumer resolves entries through findRegisteredInitializer, so an empty list makes the baseline/additional rows fall back to initializerFallbackDescription('error') → "Catalog metadata temporarily unavailable.", and the Required env vars: line stops rendering (it is inside {initializer && ...}).
  • Add controls: the select's existing registeredInitializers.length === 0 guard fires, and initializerName resolves to '', so disabled={creating || !initializerName} disables the button. A stale selectedInitializerName cannot keep it enabled — the section unmounts while loading is true, so that local state resets on remount.

Regression test: should drop the loaded catalog and disable Add when a refresh fails after a successful load (loads successfully and asserts the real description + env vars + enabled Add first, then fails the second listRegistered call). Verified it fails on the pre-fix code — the stale "Registers targets." description is still rendered — and passes with the fix.

Initializers folder: 79/79 passing (was 78 before this test), tsc --noEmit clean, eslint --max-warnings 0 clean, all re-run after the merge commit.

@feiiiiii5

Copy link
Copy Markdown
Contributor Author

Friendly ping — the last review round is fully addressed in 749e65e (refresh failure now drops the stale catalog, with the success-then-failure regression test you asked for), and the branch is merged up onto current main (acf7bb0). Happy to take another look whenever you get a chance; no rush.

Port of 20a5d7c from the pre-microsoft#2525 structure: the lookup module now
exports CatalogStatus ('loading' | 'loaded' | 'error') and
findRegisteredInitializer, which returns undefined on a miss instead of
synthesizing a placeholder. Only a settled, successful catalog load may
claim 'no longer registered'; any other status falls back to
'Catalog metadata temporarily unavailable.' via
initializerFallbackDescription.
…s page

Port of 2dc177d onto the post-microsoft#2525 structure. When the initializer
catalog request fails, ConfiguredInitializers previously synthesized a
placeholder claiming each configured initializer was 'no longer
registered' — a false statement about a startup sequence the user
cannot verify. The row now falls back to 'Catalog metadata temporarily
unavailable.', omits the env-var line (it is catalog-derived and
unknown), and only a settled, successful load may claim 'no longer
registered.'
Port of d291d77: an empty list in the dialog previously read as 'No
registered initializers were found.' even when the catalog request had
failed — a false statement about the registry. With catalogStatus the
dialog distinguishes a genuinely empty registry from an outage.
Port of 749e65e: a failed refresh previously kept the stale catalog in
state, so rows kept rendering stale descriptions and env vars, and the
available-initializers dialog stayed enabled off the stale list —
right after the user was shown a refresh error. The rejected path now
clears the catalog and flips catalogStatus to 'error'.
@feiiiiii5
fei (feiiiiii5) force-pushed the fix/initializer-catalog-unavailable branch from 26c01a9 to 8a1861f Compare September 5, 2026 01:42
@feiiiiii5

Copy link
Copy Markdown
Contributor Author

Rebased onto current main (force-pushed 8a1861f7). Roman Lutz (@romanlutz)#2525 redesigned this page while this PR was in review, so the old branch was conflicting; here is how the change was ported:

  • Kept (ported to the new structure): the catalog-state semantics — CatalogStatus ('loading'/'loaded'/'error') + findRegisteredInitializer + initializerFallbackDescription in initializerLookup.ts, wired through Initializers.tsxConfiguredInitializers (the post-MAINT: Remove additional initializer persistence #2525 home of the row rendering that previously lived in BaselineInitializers) and AvailableInitializersDialog. The failure paths this fix targets survived MAINT: Remove additional initializer persistence #2525 unchanged: a failed catalog request still left every configured row claiming "no longer registered", and a failed refresh still kept the stale catalog rendering as truth.
  • Dropped as superseded: the "disable Edit on initializer cards while the catalog is unavailable" change — MAINT: Remove additional initializer persistence #2525 removed the per-card edit capability entirely, so there is no longer an edit path to guard.
  • Commits (same semantics as the reviewed set, rebased): 941e1aed (typed catalog state), b7861c46 (distinguish unavailable vs unregistered), 72e1d955 (dialog unavailability message), 8a1861f7 (drop stale catalog on failed refresh).
  • Validation: ported tests against main's product code = 10 failed / 18 passed (pre-fix); against this branch = 28/28 passed (jest src/components/Initializers). Total diff 8 files, +150/−42.

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.

Initializer catalog failure falsely marks configured entries as unregistered

2 participants