Table of Contents

Extending Core Components — Troubleshooting

Symptom → cause → fix for the seams in Extending Core Components. Each entry is a real failure mode, not a hypothetical. If your problem isn't here, check the Error & resilience contract and the signature reference.


"My override is never called."

The most common cause, and it is silent. Checklist, in order:

  1. Signature mismatch. Several seams (and the permission/request methods that host them) are invoked by reflection, by name and exact parameter types (MibReflection.GetMethodResult…). A method whose signature doesn't match the base seam exactly is a brand-new method, not an override — it compiles and is never called. Copy the signature verbatim from the reference; keep protected virtualprotected override (or public override for ShouldHideComponent).
  2. Wrong base class. Seams live on ListComponent / FormComponent / SimpleRelatedListComponent / OrderedRelatedListComponent / ContentCriteriaComponent. A class derived from the legacy MibComponent wrapper has none of them — see the authoring guide.
  3. The type-key doesn't resolve to the Core widget. The seams are the V2/React path. If the template-component row routes to the legacy Razor Index() viewer, no seam fires. Keep the type-key resolving to the stock list/form/… widget.
  4. You overrode the wrong stage. OnFetchData only runs when ProvidesExternalData => true. Before-persist save seams don't run on a HideBulkEdit no-op. OnPermissionResolved doesn't run on Form's bulk-edit-permission-conflict throw path.

"The panel disappeared from the page."

Three different mechanisms — identify which:

  • ShouldHideComponent returned true (yours or a base you derive from). The outcome is SkippedHidden — intended.
  • A render seam threw. TransformData / ContributeSchema / OnSearchCriteriaBuilt / OnPermissionResolved exceptions are caught by ComponentEvaluator → the component is Errored and the page still renders without it. Check the BFF log for the exception.
  • Self-fetch returned an empty view. A self-fetch (ProvidesExternalData) component must render the list view path, not the empty-view path — the page render drops empty-view panels, so the React widget never mounts to call the async-data endpoint. The base classes already do this for you; only a hand-rolled GetRenderData/MapSchema override can reintroduce it.

"The list widget shows An unexpected error happened."

Your MapSchema (or a self-fetch schema you hand-built) emitted a null Filters or Configuration. The React list widget calls filters.flatMap() / find() and reads configuration.*; a null there throws in the browser. Keep both non-null (an empty Filters array and a default Configuration object are fine). Charts may be null. Prefer overriding only ContributeSchema and OnFetchData so the base keeps the contract for you.

"My computed value doesn't appear as a column."

A value you put in item.Fields["X"] in TransformData is carried but only displayed if a column with DataIndex = "X" exists. Add it in ContributeSchema (ListSchemaBuilder.For(schema).AddColumn("X", "Title")). Pair the two seams for every computed column.

"My OnFetchData filter ignores the field / operator."

You're reading context.Filter (the raw value string) instead of context.FilterCondition (the Condition with Field + Operator + Value). A drawer filter like id < 2 arrives in FilterCondition; Filter alone is just "2". See §9.

Expected — it isn't wired. For SimpleRelatedList/OrderedRelatedList, Order is never carried (the related criteria has no sort field) and the Refresh button sends no filter. Only a top-level List threads sort + filter + paging end-to-end. Track the gap on MEDIAIBOX-12033; for now do the sort/filter inside OnFetchData from Page/Limit + your own defaults, or use a List.

"A relation I added wasn't saved" / "a real edit was dropped."

  • A before-persist rule removed it: e.g. saveData.AddedRelateds.RemoveAll(...) in your OnRelatedSaveReconciled. Re-check the predicate.
  • Reconciler de-dup: CrudReconciler.Diff is last-wins per key; a duplicate key keeps the last row. SimpleSaveTemplateBase de-dups added ids but passes removed ids through verbatim.
  • ParentBatchReference nulled on a new-parent save. When the parent is being created in the same postback (ParentIDs contains 0), related rows reference it through ParentBatchReference; clearing it throws InvalidParentComponentReferenceException. Leave it set (default = the parent template-component key) — see RelatedSaveBuilder.

"A self-fetch list renders for users without read, with noisy log exceptions."

This was a defect in the first cut of the self-fetch path: a self-fetch related/ordered list has no relateds view-data, and the permission computation dereferenced it, NRE'd, and fell back to a read-allowed permission while logging the exception on every render. It is fixed — ensure you're on a build that includes the fix. When overriding OnPermissionResolved on a self-fetch list, do not assume the relateds-only inputs (RelatedCopyType, RemoveButtonBehavior) were evaluated — they're skipped because there is no relateds response.

"My after-save side effect failed and the whole save errored."

An after-persist seam (OnAfterSaveResponseBuilt / OnAfterDeleteResponseBuilt) runs after the entity is committed — a throw there only breaks the response build, not the data, but it still surfaces as an error to the user. Guard best-effort side effects and downgrade rather than throw:

if (response.Status == PostbackStatus.Error) return;   // don't act on a failed save
try { _kafka.Publish(...); }
catch (Exception ex) { response.Status = PostbackStatus.SuccessWithWarning; /* log ex */ }

For observable/rollback-able chains use PostSaveChain/SaveTransactionContext.

"My save-rejection message shows as a generic error."

Throw the localized form from a before-persist save seam: throw new PersistenceValidationException(TemplateComponentKey, "{DICT:ERROR/YOUR_KEY}"). A plain Exception surfaces as a generic error; only PersistenceValidationException with a {DICT:…} key renders a clean localized message.

"RelatedListSchema.Title won't compile."

Its setter is internal — a customer-assembly override can't assign it. Set the related list's title through configuration, not the schema object. (ListSchema exposes the title as Name, which is settable.)

"new FormFieldSchema { ... } won't compile."

Type, Config, and Props are C# required members — set all three. Type is the React variant (serialized as variant). Form fields have no Name/DataIndex; select them with FormSchemaBuilder.OverrideWhere(predicate, …).

"I get a 500 instead of a degraded panel."

Render-seam throws are caught and degrade one panel. But a throw while building the secondary information requests (where OnSearchCriteriaBuilt runs) is handled by a different guard that logs and skips that panel's secondary data; OperationCanceledException is re-thrown (a real cancellation must propagate). If you see a hard 500, it's likely outside the seam — check the BFF log for the stack and the failing stage.