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:
- 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; keepprotected virtual→protected override(orpublic overrideforShouldHideComponent). - Wrong base class. Seams live on
ListComponent/FormComponent/SimpleRelatedListComponent/OrderedRelatedListComponent/ContentCriteriaComponent. A class derived from the legacyMibComponentwrapper has none of them — see the authoring guide. - 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 stocklist/form/… widget. - You overrode the wrong stage.
OnFetchDataonly runs whenProvidesExternalData => true. Before-persist save seams don't run on aHideBulkEditno-op.OnPermissionResolveddoesn't run on Form's bulk-edit-permission-conflict throw path.
"The panel disappeared from the page."
Three different mechanisms — identify which:
ShouldHideComponentreturnedtrue(yours or a base you derive from). The outcome isSkippedHidden— intended.- A render seam threw.
TransformData/ContributeSchema/OnSearchCriteriaBuilt/OnPermissionResolvedexceptions are caught byComponentEvaluator→ the component isErroredand 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-rolledGetRenderData/MapSchemaoverride 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.
"Sort / refresh-time filter never reaches OnFetchData on a related or ordered list."
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 yourOnRelatedSaveReconciled. Re-check the predicate. - Reconciler de-dup:
CrudReconciler.Diffis last-wins per key; a duplicate key keeps the last row.SimpleSaveTemplateBasede-dups added ids but passes removed ids through verbatim. ParentBatchReferencenulled on a new-parent save. When the parent is being created in the same postback (ParentIDscontains0), related rows reference it throughParentBatchReference; clearing it throwsInvalidParentComponentReferenceException. Leave it set (default = the parent template-component key) — seeRelatedSaveBuilder.
"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.