Context
Upstream langflow-ai/langflow#14312 (LE-2037 / langflow-ai/langflow#14096) fixes duplicate DOM ids on node parameter fields: an input's id was derived from the template type/name alone, so two nodes exposing a field with the same name rendered multiple elements sharing one id — a WCAG 4.1.1 (ID uniqueness) violation that also prevents browser autofill from resolving the field. The fix scopes the rendered id by nodeId (<id>-<nodeId>) and deliberately leaves data-testid unscoped, which is what this suite selects on.
The suite is structurally blind to this class of defect
Verified by a full sweep of tests/:
- No id-uniqueness assertion anywhere. Raw
#id / [id=...] selectors exist only for app-chrome singletons ([id="new-project-btn"], #is_active, #body.dark) — never for node fields.
- No accessibility tooling. Grep for
axe-core|AxeBuilder|injectAxe|checkA11y|pa11y|lighthouse → 0 hits. No page.accessibility usage.
- The fixture cannot surface it.
tests/fixtures/fixtures.ts registers one page.on("response") listener (4xx/5xx + flow-execution errors). There is no console or pageerror handler — and neither would help: Chrome reports "Duplicate form field id in the same form" through the CDP Audits domain that feeds the DevTools Issues panel, not as a console message.
Several specs came close and were designed around the ambiguity instead of catching it — most tellingly core-components/chat-input-output-component-regression.spec.ts:251-261, where Chat Input and Chat Output both render popover-anchor-input-sender_name, the authors hit the double match, diagnosed it as "DOM ordering", and worked around it with a .react-flow__node-scoped filter. Others actively mask the class (flow-functionality/generalBugs-shard-7.spec.ts:54 — getByTestId(/^popover-anchor-input-base_url.*/).nth(0)). None of that is wrong as written; it just means an id collision cannot fail anything today.
Why this deserves its own spec even though upstream added one
The fix ships src/frontend/tests/core/regression/duplicateDomIds.spec.ts upstream. That runs in Langflow's CI against the PR's own code; ours runs against published images. Different nets: theirs catches the author, ours catches what reached the user. Plus:
- The fix is opt-in, not a chokepoint.
getNodeScopedDomId(id, nodeId) must be called manually at each id={...} site — 15 renderer files were edited one by one. The sixteenth renderer someone adds will not call it, and the defect returns silently. nodeId is optional and falls back to the unscoped id, so a renderer that merely fails to receive the prop regresses without a signal.
- The gap is class-level, not case-level. Adding one assertion closes a category.
- The reverse guard is the more valuable half. Asserting that
getByTestId(...) still resolves to both nodes protects the contract 132 call sites across 45 specs depend on. If uniqueness is ever "fixed" by scoping data-testid instead of id, this spec fails first — before 45 files fail at once.
Cost profile: deterministic, no provider credentials, no LLM call, no network beyond the Langflow instance.
Scope
tests/tests-automations/regression/core-components/duplicate-dom-ids-regression.spec.ts — two cases, both placing two nodes that expose the same field, sweeping input[id], textarea[id], select[id] for duplicates and asserting the field's testid still resolves to 2 elements:
- A — two API Request nodes /
popover-anchor-input-url_input (StrRenderComponent → InputComponent → popover).
- B — two Agent nodes /
textarea_str_system_prompt (TextAreaComponent).
docs/core-components/duplicate-dom-ids-regression.md — mirrored spec doc.
QA-CHECKLIST.md §2.1 bullet (manual Part II only — generated blocks untouched).
Subject choice is constrained: Chat Input and Webhook are singletons (adding one removes the other's sidebar +; they cannot be duplicated or pasted — which is why upstream had to build its ChatInput case via the API), and Prompt Template does not reproduce the defect because its visible field is a prompt-modal surface, not a form control.
The sweep is scoped to form controls on purpose: that is what the reported DevTools warning covers and what breaks autofill. Icon SVGs legitimately repeat their own internal ids (gradients, masks, filters) whenever the same icon renders twice — a separate concern that must not fail this test for the wrong reason.
Timing — the current nightly is still pre-fix
Confirmed by a live DOM sweep on nightly 1.12.0.dev9, two API Request nodes on canvas:
duplicates: ["popover-anchor-input-url_input x2"]
So today's image is a negative control, not a passing target: the spec fails by design on it, which satisfies the CONTRIBUTING "force a failure to confirm no false positives" step for free. The fix is merged upstream and lands in the next nightly.
This decides the @stable question — merging as @stable against a pre-fix nightly would turn daily-stable.yml red every weekday and open a daily-failure issue per run. Options, to settle on the PR:
- A — merge with
@stable only once langflowai/langflow-nightly:latest carries the fix.
- B — merge now without
@stable (checklist bullet [-], reason stated in the spec doc's Tags section), promote in a follow-up.
Validation
- Fails on the current pre-fix nightly, with the failure message naming the offending ids.
- Passes on a build containing the upstream fix.
toHaveCount(2) gates are reached (they prevent a vacuous pass on a half-mounted canvas).
- Id-scoped flow teardown verified — no leaked flows after the run.
- No
🚨 Backend Error:; npm run typecheck, npm run lint, npm run validate:specs, npm run check:checklist-coverage.
Not a REGRESSIONS.md row
REGRESSIONS.md admits a row only on a confirmed langflow-regression verdict traced to a spec failure or spec-validation run. This defect was found by hand via the DevTools Issues panel, not by the suite, so per that file's own rule it stays on the Jira board. If this new spec later catches a reintroduction on a published build, that occurrence qualifies.
Process
Off-wave: there is no open milestone, so this is filed as an approved follow-up exception per CLAUDE.md → What to work on.
Context
Upstream langflow-ai/langflow#14312 (LE-2037 / langflow-ai/langflow#14096) fixes duplicate DOM ids on node parameter fields: an input's
idwas derived from the template type/name alone, so two nodes exposing a field with the same name rendered multiple elements sharing oneid— a WCAG 4.1.1 (ID uniqueness) violation that also prevents browser autofill from resolving the field. The fix scopes the renderedidbynodeId(<id>-<nodeId>) and deliberately leavesdata-testidunscoped, which is what this suite selects on.The suite is structurally blind to this class of defect
Verified by a full sweep of
tests/:#id/[id=...]selectors exist only for app-chrome singletons ([id="new-project-btn"],#is_active,#body.dark) — never for node fields.axe-core|AxeBuilder|injectAxe|checkA11y|pa11y|lighthouse→ 0 hits. Nopage.accessibilityusage.tests/fixtures/fixtures.tsregisters onepage.on("response")listener (4xx/5xx + flow-execution errors). There is no console orpageerrorhandler — and neither would help: Chrome reports "Duplicate form field id in the same form" through the CDPAuditsdomain that feeds the DevTools Issues panel, not as a console message.Several specs came close and were designed around the ambiguity instead of catching it — most tellingly
core-components/chat-input-output-component-regression.spec.ts:251-261, where Chat Input and Chat Output both renderpopover-anchor-input-sender_name, the authors hit the double match, diagnosed it as "DOM ordering", and worked around it with a.react-flow__node-scoped filter. Others actively mask the class (flow-functionality/generalBugs-shard-7.spec.ts:54—getByTestId(/^popover-anchor-input-base_url.*/).nth(0)). None of that is wrong as written; it just means an id collision cannot fail anything today.Why this deserves its own spec even though upstream added one
The fix ships
src/frontend/tests/core/regression/duplicateDomIds.spec.tsupstream. That runs in Langflow's CI against the PR's own code; ours runs against published images. Different nets: theirs catches the author, ours catches what reached the user. Plus:getNodeScopedDomId(id, nodeId)must be called manually at eachid={...}site — 15 renderer files were edited one by one. The sixteenth renderer someone adds will not call it, and the defect returns silently.nodeIdis optional and falls back to the unscoped id, so a renderer that merely fails to receive the prop regresses without a signal.getByTestId(...)still resolves to both nodes protects the contract 132 call sites across 45 specs depend on. If uniqueness is ever "fixed" by scopingdata-testidinstead ofid, this spec fails first — before 45 files fail at once.Cost profile: deterministic, no provider credentials, no LLM call, no network beyond the Langflow instance.
Scope
tests/tests-automations/regression/core-components/duplicate-dom-ids-regression.spec.ts— two cases, both placing two nodes that expose the same field, sweepinginput[id], textarea[id], select[id]for duplicates and asserting the field's testid still resolves to 2 elements:popover-anchor-input-url_input(StrRenderComponent→InputComponent→popover).textarea_str_system_prompt(TextAreaComponent).docs/core-components/duplicate-dom-ids-regression.md— mirrored spec doc.QA-CHECKLIST.md§2.1 bullet (manual Part II only — generated blocks untouched).Subject choice is constrained: Chat Input and Webhook are singletons (adding one removes the other's sidebar
+; they cannot be duplicated or pasted — which is why upstream had to build itsChatInputcase via the API), and Prompt Template does not reproduce the defect because its visible field is a prompt-modal surface, not a form control.The sweep is scoped to form controls on purpose: that is what the reported DevTools warning covers and what breaks autofill. Icon SVGs legitimately repeat their own internal ids (gradients, masks, filters) whenever the same icon renders twice — a separate concern that must not fail this test for the wrong reason.
Timing — the current nightly is still pre-fix
Confirmed by a live DOM sweep on nightly
1.12.0.dev9, two API Request nodes on canvas:So today's image is a negative control, not a passing target: the spec fails by design on it, which satisfies the CONTRIBUTING "force a failure to confirm no false positives" step for free. The fix is merged upstream and lands in the next nightly.
This decides the
@stablequestion — merging as@stableagainst a pre-fix nightly would turndaily-stable.ymlred every weekday and open adaily-failureissue per run. Options, to settle on the PR:@stableonly oncelangflowai/langflow-nightly:latestcarries the fix.@stable(checklist bullet[-], reason stated in the spec doc's Tags section), promote in a follow-up.Validation
toHaveCount(2)gates are reached (they prevent a vacuous pass on a half-mounted canvas).🚨 Backend Error:;npm run typecheck,npm run lint,npm run validate:specs,npm run check:checklist-coverage.Not a REGRESSIONS.md row
REGRESSIONS.mdadmits a row only on a confirmedlangflow-regressionverdict traced to a spec failure or spec-validation run. This defect was found by hand via the DevTools Issues panel, not by the suite, so per that file's own rule it stays on the Jira board. If this new spec later catches a reintroduction on a published build, that occurrence qualifies.Process
Off-wave: there is no open milestone, so this is filed as an approved
follow-upexception perCLAUDE.md→ What to work on.