Where: assets/profiler-ui.js:421-425 (the column-mapping change handler) vs. reprofile() (194-205, whose success/failure return value was added specifically for this class of problem in #419) and profiler.html's DOM structure (#mappingBlock, #thresholdControls, #crossControls are all descendants of #results, lines ~188-276).
The gap: assets/profiler-engine.js throws ("cross column(s) don't match any profiled dimension") when opts.cross names a dimension that no longer exists - the fix for #420. But currentOpts.cross is only ever set by the cross-selector dropdowns; nothing clears or revalidates it when a column-mapping override removes that same dimension instead. The mapping-list change handler calls reprofile(false) and ignores its boolean return value entirely - unlike the reference-upload handler, which was patched in #419 to check it. When reprofile() catches the thrown error, showError() sets results.hidden = true - and since the mapping controls themselves live inside #results, they disappear along with everything else.
Repro (engine behavior confirmed directly):
require('./assets/profiler-engine.js');
var E = globalThis.FairCodeProfiler;
var table = { columns: ['sex','race','age'], rows: /* 300 rows with sex/race/age */ };
E.profile(table, {}, {cross: ['race','age']}); // OK
E.profile(table, {race: 'ignore'}, {cross: ['race','age']}); // throws:
// "cross column(s) don't match any profiled dimension: race"
In the live UI: pick a custom cross of race x age, then in Column mapping re-map race to "Not demographic" - the mapping handler re-profiles with the now-stale cross, the engine throws, and the whole results section - including the mapping dropdown needed to undo the change - vanishes.
Why it matters: the only recovery is dropping the file again from scratch, losing all column-mapping and threshold customization - a worse outcome than the reference-upload path, which got exactly this class of fix in #419.
Suggested fix: in the mapping change handler, clear or revalidate currentOpts.cross against the new set of dimension names before calling reprofile, and/or check reprofile()'s return value the same way the reference-upload handler already does.
Where:
assets/profiler-ui.js:421-425(the column-mappingchangehandler) vs.reprofile()(194-205, whose success/failure return value was added specifically for this class of problem in #419) andprofiler.html's DOM structure (#mappingBlock,#thresholdControls,#crossControlsare all descendants of#results, lines ~188-276).The gap:
assets/profiler-engine.jsthrows ("cross column(s) don't match any profiled dimension") whenopts.crossnames a dimension that no longer exists - the fix for #420. ButcurrentOpts.crossis only ever set by the cross-selector dropdowns; nothing clears or revalidates it when a column-mapping override removes that same dimension instead. The mapping-listchangehandler callsreprofile(false)and ignores its boolean return value entirely - unlike the reference-upload handler, which was patched in #419 to check it. Whenreprofile()catches the thrown error,showError()setsresults.hidden = true- and since the mapping controls themselves live inside#results, they disappear along with everything else.Repro (engine behavior confirmed directly):
In the live UI: pick a custom cross of race x age, then in Column mapping re-map
raceto "Not demographic" - the mapping handler re-profiles with the now-stale cross, the engine throws, and the whole results section - including the mapping dropdown needed to undo the change - vanishes.Why it matters: the only recovery is dropping the file again from scratch, losing all column-mapping and threshold customization - a worse outcome than the reference-upload path, which got exactly this class of fix in #419.
Suggested fix: in the mapping
changehandler, clear or revalidatecurrentOpts.crossagainst the new set of dimension names before callingreprofile, and/or checkreprofile()'s return value the same way the reference-upload handler already does.