Skip to content

fix: don't let a single unrenderable input abort the whole workflow render - #160

Merged
timurbazhirov merged 1 commit into
mainfrom
fix/execution-unit-input-render-graceful-degradation
Jul 29, 2026
Merged

fix: don't let a single unrenderable input abort the whole workflow render#160
timurbazhirov merged 1 commit into
mainfrom
fix/execution-unit-input-render-graceful-degradation

Conversation

@timurbazhirov

Copy link
Copy Markdown
Member

Summary

  • ExecutionUnitInput.render() rethrew on any Nunjucks template error. That propagates uncaught through ExecutionUnit.render() -> Subworkflow.render() -> Workflow.render() -> job.render(), which job-designer's JOB_WORKFLOW_SYNC/JOB_UPDATE reducers call unconditionally.
  • Selecting a multi-material workflow (e.g. Valence Band Offset, whose interface-left/right units are keyed by MATERIAL_INDEX "1"/"2" into input.perMaterial) while the job still only has its single default material makes perMaterial[1]/[2] undefined, which the sprintf filter throws on - crashing the reducer and silently aborting the workflow selection itself. job-designer's onSelectWorkflowsSubmit only console.errors the rejected promise, so the UI just stays on the old workflow with no visible error to the user.
  • Fix: fall back to the raw (unrendered) template on error instead of rethrowing, matching the function's existing this.rendered = rendered || this.template.content fallback for empty output. The debug console.error logging is preserved. The next job/material update re-runs render() and produces a correct preview once enough materials are assigned.

Root cause chain (for reviewers)

  1. reference/job-designer/src/reducers/renderJobForDesignerState.js calls job.render() unconditionally on every JOB_UPDATE/JOB_WORKFLOW_SYNC.
  2. Workflow.render() -> Subworkflow.render() -> ExecutionUnit.render() render every unit in every subworkflow, not just the one currently visible.
  3. VBO's standata definition (reference/standata/data/workflows/workflows/espresso/valence_band_offset.json) has "Set Material Index" assignment units setting MATERIAL_INDEX to "0"/"1"/"2" for its combined/left/right branches, and several unit templates guard on subworkflowContext.MATERIAL_INDEX to index into input.perMaterial[...].
  4. Right after selecting this workflow (before the user assigns the 3 required materials), the job still has 1 material, so perMaterial has only index 0 - perMaterial[1]/perMaterial[2] are undefined.
  5. ExecutionUnitInput.render() calls the custom sprintf filter (reference/standata/src/js/utils/template.ts) with undefined, which throws TypeError: [sprintf] expecting number but found undefined - previously uncaught here, now handled.

Test plan

  • npm test in reference/wode - 47/47 passing, no regressions.
  • Live repro in a real browser session against a local web-app + Meteor server: before the fix, selecting "Valence Band Offset (2D)" on a job with the default single material silently kept the old "Total Energy" workflow active (confirmed via DOM inspection + matching console errors). After the fix (verified via a locally patched node_modules copy + full rspack cache clear/restart), the workflow correctly switches, all 7 VBO subworkflows populate, and the "average ESP" unit (the element job-espresso-valence-band-offset.feature needs) is present and clickable.
  • Re-ran cypress run --spec cypress/e2e/jobs/run/job-espresso-valence-band-offset.feature in web-app: previously failed at "I open 'average ESP' subworkflow unit" (never found); now progresses past that entirely into a later, unrelated bug (duplicate "cutoffs" panel in Important Settings causing an ambiguous cy.focus() match - tracked separately, not part of this PR).

🤖 Generated with Claude Code

…ender

ExecutionUnitInput.render() rethrew on any Nunjucks template error. That
propagates uncaught through ExecutionUnit.render() -> Subworkflow.render()
-> Workflow.render() -> job.render(), which job-designer's JOB_WORKFLOW_SYNC
and JOB_UPDATE reducers call unconditionally. Selecting a multi-material
workflow (e.g. Valence Band Offset, whose interface-left/right units are
keyed by MATERIAL_INDEX "1"/"2" into input.perMaterial) while the job still
only has its single default material makes perMaterial[1]/[2] undefined,
which the sprintf filter throws on - crashing the reducer and silently
aborting the workflow selection itself (job-designer's
onSelectWorkflowsSubmit only console.errors the rejected promise, so the UI
just stays on the old workflow with no visible error).

Fall back to the raw template on render error instead of rethrowing, so the
reducer completes and the workflow selection actually applies. The next
job/material update re-runs render() and produces a correct preview once
enough materials are assigned.

@VsevolodX VsevolodX left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM

@timurbazhirov
timurbazhirov merged commit c0f12bc into main Jul 29, 2026
9 checks passed
@timurbazhirov
timurbazhirov deleted the fix/execution-unit-input-render-graceful-degradation branch July 29, 2026 22:21
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.

2 participants