Skip to content

fix: guard input-template render effect against unstable content - #7

Merged
timurbazhirov merged 2 commits into
mainfrom
fix/execution-unit-input-render-loop
Jul 21, 2026
Merged

fix: guard input-template render effect against unstable content#7
timurbazhirov merged 2 commits into
mainfrom
fix/execution-unit-input-render-loop

Conversation

@timurbazhirov

Copy link
Copy Markdown
Member

Summary

ExecutionUnit's effect that re-renders input-template content whenever renderingContext changes always wrote the freshly-rendered content back via onUnitInputRenderedUpdate, even when it exactly matched what was already stored:

useEffect(() => {
    if (!renderingContext) return;
    unit.input.forEach((inputRow, index) => {
        const rendered = renderTextWithSubstitutes(inputRow.template.content, renderingContext);
        onUnitInputRenderedUpdate(index, rendered); // always called
    });
}, [renderingContext]);

onUnitInputRenderedUpdate always produces a brand-new unit object via onUpdate(...). If anything upstream recomputes renderingContext from the updated unit (even to a referentially-new-but-otherwise-equal value), this effect re-fires — and for any input whose rendered output isn't perfectly stable across renders, this creates an infinite renderingContextonUpdate → new renderingContext loop, crashing with React's "Maximum update depth exceeded".

Reproduced live: selecting the pp.x or average.x executables (unlike the default pw.x) in the unit editor crashes the whole app with this exact error, immediately on opening the Flavor select.

Fix

Only call onUnitInputRenderedUpdate when the newly-rendered content actually differs from inputRow.rendered, breaking the loop regardless of why a given input's rendered output is unstable.

Test plan

  • npm run build and npm test (20/20 passing)
  • Reproduced and confirmed the crash live against a deployed environment (Cypress spec unit-editor-choose-espresso-executable-and-flavor.feature, examples using pp.x/average.x)

🤖 Generated with Claude Code

The effect syncing rendered input-template content whenever
renderingContext changes always wrote the freshly-rendered content
back via onUnitInputRenderedUpdate, even when it matched what was
already stored. onUnitInputRenderedUpdate always produces a new unit
object, which can cause renderingContext to be recomputed upstream
and re-fire this same effect - for any input whose rendered output
isn't stable across renders, this creates an infinite update loop
("Maximum update depth exceeded"), reproduced when selecting the
pp.x/average.x executables in the unit editor. Only write back when
the rendered content actually changed.
@timurbazhirov
timurbazhirov merged commit db574d0 into main Jul 21, 2026
4 checks passed
@timurbazhirov
timurbazhirov deleted the fix/execution-unit-input-render-loop branch July 21, 2026 08:52
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.

1 participant