Operator pages show a failed read as an empty or settled answer instead of an error #207

Open
opened 2026-10-09 16:03:03 +00:00 by cgalo5758 · 1 comment
Owner

What happens

This is the operator side of #197. Several operator pages treat a failed database read as an empty answer. The section shows its empty state, or a verdict built from the missing rows, and nothing on the page says a read failed. Most of these reads leave no log line.

  • Products list and product page. buildProductListViewModels logs each failed batch read and builds the rows as if the read had returned nothing:

    • A failed product-shape read makes every published product read Incomplete, "Missing: entitlement set".
    • A failed price read makes a listed product read "Missing: Active price". A failed price-mapping read makes it read "Missing: payment processing".
    • A failed read of the entitlement sets' rules or of the resource keys leaves the check that the product's provider is configured with nothing to check. A product whose provider is not configured then reads Purchasable.

    The product page's readiness panel (loadProductEditDataResult, loadProviderReadinessRows) does the same with its own reads. After a failed mapping read it says the price is not registered with Stripe and offers Sync to Stripe. After a failed ladder read it says "Not a tier on any ladder."

  • Organization page. loadOrgEnrollmentData shows "Organization not found" for any error reading the organization, including a database fault.

    • Pools panel. A failed read of a pool's plan positions shows "No plan is delivering into this pool". A failed product, ladder or tier read shows a delivery with no product name, on a ladder with no name, at "rank 0", the ladder's bottom tier. A failed usage read drops the usage table.
    • Billing summary. A failed account read shows "No billing account exists for this organization yet." Failed subscription, invoice and balance reads show "No subscriptions", "No invoices issued" and a zero balance.
    • Issue grant form. A failed read in subscriptionHeldLadders drops the ⚠ warning that a grant would replace a tier a subscription pays for.

    None of these reads is logged.

  • Grants page and the organization's grants ledger. buildGrantViewModels drops name-read errors without logging them.

    • A failed person read shows "System" under Granted by, although a person issued the grant.
    • A failed product read leaves the product blank. The ledger shows "—", and the Grants page labels the row "Entitlement set".
    • A failed organization read leaves the Grants page's organization link with no text.
  • Plan topology and the plan ladders list. loadPlanTopologyData discards the errors of the reads it makes for each ladder:

    • A failed count shows "0 organizations".
    • A failed tier read empties the ladder's column and lists its products as off-ladder.
    • Failed shared-product and rank-0 reads show "No products are shared across ladders." and "No default (off-ladder)" for every org type.

    loadPlanLaddersPageData shows 0 tiers and 0 organizations when a ladder's reads fail, and offers Delete, which the delete handler then refuses after counting again.

  • Billing. The invoice page (loadOperatorInvoiceDetailData) answers 404, "There is no operator page at …", for any failed read of the invoice, not only a missing invoice, and logs nothing. The billing lists hide rows recorded in the Stripe mode (test or live) that the configured key is not in. When resolveEnvFilter fails to read which rows those are, it logs a warning and hides nothing. The other mode's rows then appear among the key's own, unmarked, and no line says they are there.

Some sections already report their own failure. On the organization page, Tier changes, Members, Grants and Entitlement changes show a load error in place of their content. The product page answers "Could not load this product right now. Try again." when the product read fails.

What should happen

When a read fails, the error is logged and the page reports the failure where the answer would have appeared:

  • A section shows its load error in place of its empty state.
  • A verdict or count that depends on the read says it could not be read; it does not state a result.
  • A name that could not be read does not turn into "System", "Entitlement set" or "rank 0".
  • A record named in the URL that cannot be read gets the answer the product page gives. Only a missing row answers "not found".

Where

  • internal/server/operator_products.go: buildProductListViewModels, loadProductEditDataResult, loadProviderReadinessRows
  • internal/server/operator_enrollment.go: loadOrgEnrollmentData, subscriptionHeldLadders
  • internal/server/operator_partials.go: buildGrantViewModels
  • internal/server/operator_topology.go: loadPlanTopologyData
  • internal/server/operator_plan_ladders.go: loadPlanLaddersPageData
  • internal/server/operator_billing.go: loadOperatorInvoiceDetailData, resolveEnvFilter

The batch name reads #10 asks for would replace the per-row name reads on the grants and organization pages, so the name cases can be fixed with that work.

Why it matters

During a passing database fault, an operator can be told any of these:

  • a product is purchasable when nothing checked its provider
  • an organization does not exist, or has no plan or billing account
  • the system issued a grant that a person issued

The operator can act on these. For example, they can issue a grant over a tier a subscription pays for without seeing the warning that the subscription keeps billing. Most of these reads leave no log line, so nothing records the fault behind the claim.

## What happens This is the operator side of #197. Several operator pages treat a failed database read as an empty answer. The section shows its empty state, or a verdict built from the missing rows, and nothing on the page says a read failed. Most of these reads leave no log line. - **Products list and product page.** `buildProductListViewModels` logs each failed batch read and builds the rows as if the read had returned nothing: - A failed product-shape read makes every published product read Incomplete, "Missing: entitlement set". - A failed price read makes a listed product read "Missing: Active price". A failed price-mapping read makes it read "Missing: payment processing". - A failed read of the entitlement sets' rules or of the resource keys leaves the check that the product's provider is configured with nothing to check. A product whose provider is not configured then reads Purchasable. The product page's readiness panel (`loadProductEditDataResult`, `loadProviderReadinessRows`) does the same with its own reads. After a failed mapping read it says the price is not registered with Stripe and offers Sync to Stripe. After a failed ladder read it says "Not a tier on any ladder." - **Organization page.** `loadOrgEnrollmentData` shows "Organization not found" for any error reading the organization, including a database fault. - **Pools panel.** A failed read of a pool's plan positions shows "No plan is delivering into this pool". A failed product, ladder or tier read shows a delivery with no product name, on a ladder with no name, at "rank 0", the ladder's bottom tier. A failed usage read drops the usage table. - **Billing summary.** A failed account read shows "No billing account exists for this organization yet." Failed subscription, invoice and balance reads show "No subscriptions", "No invoices issued" and a zero balance. - **Issue grant form.** A failed read in `subscriptionHeldLadders` drops the ⚠ warning that a grant would replace a tier a subscription pays for. None of these reads is logged. - **Grants page and the organization's grants ledger.** `buildGrantViewModels` drops name-read errors without logging them. - A failed person read shows "System" under Granted by, although a person issued the grant. - A failed product read leaves the product blank. The ledger shows "—", and the Grants page labels the row "Entitlement set". - A failed organization read leaves the Grants page's organization link with no text. - **Plan topology and the plan ladders list.** `loadPlanTopologyData` discards the errors of the reads it makes for each ladder: - A failed count shows "0 organizations". - A failed tier read empties the ladder's column and lists its products as off-ladder. - Failed shared-product and rank-0 reads show "No products are shared across ladders." and "No default (off-ladder)" for every org type. `loadPlanLaddersPageData` shows 0 tiers and 0 organizations when a ladder's reads fail, and offers Delete, which the delete handler then refuses after counting again. - **Billing.** The invoice page (`loadOperatorInvoiceDetailData`) answers 404, "There is no operator page at …", for any failed read of the invoice, not only a missing invoice, and logs nothing. The billing lists hide rows recorded in the Stripe mode (test or live) that the configured key is not in. When `resolveEnvFilter` fails to read which rows those are, it logs a warning and hides nothing. The other mode's rows then appear among the key's own, unmarked, and no line says they are there. Some sections already report their own failure. On the organization page, Tier changes, Members, Grants and Entitlement changes show a load error in place of their content. The product page answers "Could not load this product right now. Try again." when the product read fails. ## What should happen When a read fails, the error is logged and the page reports the failure where the answer would have appeared: - A section shows its load error in place of its empty state. - A verdict or count that depends on the read says it could not be read; it does not state a result. - A name that could not be read does not turn into "System", "Entitlement set" or "rank 0". - A record named in the URL that cannot be read gets the answer the product page gives. Only a missing row answers "not found". ## Where - `internal/server/operator_products.go`: `buildProductListViewModels`, `loadProductEditDataResult`, `loadProviderReadinessRows` - `internal/server/operator_enrollment.go`: `loadOrgEnrollmentData`, `subscriptionHeldLadders` - `internal/server/operator_partials.go`: `buildGrantViewModels` - `internal/server/operator_topology.go`: `loadPlanTopologyData` - `internal/server/operator_plan_ladders.go`: `loadPlanLaddersPageData` - `internal/server/operator_billing.go`: `loadOperatorInvoiceDetailData`, `resolveEnvFilter` The batch name reads #10 asks for would replace the per-row name reads on the grants and organization pages, so the name cases can be fixed with that work. ## Why it matters During a passing database fault, an operator can be told any of these: - a product is purchasable when nothing checked its provider - an organization does not exist, or has no plan or billing account - the system issued a grant that a person issued The operator can act on these. For example, they can issue a grant over a tier a subscription pays for without seeing the warning that the subscription keeps billing. Most of these reads leave no log line, so nothing records the fault behind the claim.
Author
Owner

More operator pages show a failed read the same way:

  • Billing lists. The billing accounts, subscriptions, invoices and payments lists (internal/server/operator_billing.go) read each row's organization and Stripe mapping one row at a time. A failed organization read leaves the row without the organization's name, and a failed mapping read shows the row as not mapped. The customer lookup in internal/server/operator.go loses a candidate's organization name the same way.
  • Setup step and entitlement sets. The setup step (internal/server/setup_state.go) skips a set whose rules cannot be read, so it can read complete when every rule read fails. The entitlement sets list (internal/server/operator_entitlement_sets.go) shows 0 rules for a set whose rules could not be read.
  • Product prices. The prices table on the product page (internal/server/operator_billing.go) shows a price as not mapped when its mapping read fails.
More operator pages show a failed read the same way: - **Billing lists.** The billing accounts, subscriptions, invoices and payments lists (`internal/server/operator_billing.go`) read each row's organization and Stripe mapping one row at a time. A failed organization read leaves the row without the organization's name, and a failed mapping read shows the row as not mapped. The customer lookup in `internal/server/operator.go` loses a candidate's organization name the same way. - **Setup step and entitlement sets.** The setup step (`internal/server/setup_state.go`) skips a set whose rules cannot be read, so it can read complete when every rule read fails. The entitlement sets list (`internal/server/operator_entitlement_sets.go`) shows 0 rules for a set whose rules could not be read. - **Product prices.** The prices table on the product page (`internal/server/operator_billing.go`) shows a price as not mapped when its mapping read fails.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: wiki-cafe/member-console#207