{"record":{"id":"3757227883af68eb","repo":"windmill-labs/windmill","slug":"duplicate-module-ids-found-in-flow-375722","errorCode":null,"errorMessage":"Duplicate module IDs found in flow","messagePattern":"Duplicate module IDs found in flow","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"frontend/src/lib/components/copilot/chat/flow/editableFlowJson.ts","lineNumber":256,"sourceCode":"\t\t\t\t\tconst expectedFormat = getExpectedFormat(targetSchema)\n\t\t\t\t\tif (expectedFormat) {\n\t\t\t\t\t\tmessage += `\\n    Expected format: ${expectedFormat}`\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (moduleId) {\n\t\t\t\treturn `Module \"${moduleId}\" -> ${fieldPath}: ${message}`\n\t\t\t}\n\t\t\treturn `${path.join('.')}: ${message}`\n\t\t})\n\n\t\tthrow new Error(`Invalid flow modules:\\n${errors.join('\\n')}`)\n\t}\n\n\tconst ids = collectAllFlowModuleIdsFromModules(parsedModules)\n\tif (ids.length !== new Set(ids).size) {\n\t\tthrow new Error('Duplicate module IDs found in flow')\n\t}\n\n\tconst reservedIds = ids.filter(\n\t\t(id) => id === SPECIAL_MODULE_IDS.PREPROCESSOR || id === SPECIAL_MODULE_IDS.FAILURE\n\t)\n\tif (reservedIds.length > 0) {\n\t\tthrow new Error(\n\t\t\t'Special modules must be provided via preprocessor_module and failure_module, not inside modules'\n\t\t)\n\t}\n\n\t// Not expressible in the schema: `provider` is required only when the step is standalone, and\n\t// making AiAgent a conditional union breaks the FlowModuleValue discriminated union it belongs to.\n\tconst providerless = collectProviderlessAgentIds(parsedModules)\n\tif (providerless.length > 0) {\n\t\tthrow new Error(\n\t\t\t`AI agent modules ${providerless\n\t\t\t\t.map((id) => `\"${id}\"`)","sourceCodeStart":238,"sourceCodeEnd":274,"githubUrl":"https://github.com/windmill-labs/windmill/blob/e474e8803ce2ff5c2df09a58dab51d45f5c922ca/frontend/src/lib/components/copilot/chat/flow/editableFlowJson.ts#L238-L274","documentation":"Inside validateFlowModules, after schema validation succeeds, all module IDs (collected recursively, including nested branch/loop children via collectAllFlowModuleIdsFromModules) are checked for uniqueness with a Set. Any repetition throws 'Duplicate module IDs found in flow', guarding the graph invariant that every step is uniquely addressable.","triggerScenarios":"validateFlowModules called with a modules array where the same id occurs twice at any depth — two siblings, a parent and nested child, or duplicates across branch arms.","commonSituations":"Duplicating a branch arm without renaming child IDs; template concatenation appending an already-present module; AI regeneration reusing a previous step's ID for a new step.","solutions":["Rename every duplicate to a fresh unique ID and re-validate.","Remember IDs are checked recursively — inspect nested children inside for/branch-all/branch-one modules too.","De-duplicate programmatically before submitting (e.g. walk the tree and assert Set(ids).size === ids.length)."],"exampleFix":"// before: same id in two branch arms\n[{ id: 'a', value: { type: 'branchone', ... } }] // both arms contain { id: 'x' }\n// after: rename one occurrence\narms[0].modules[0].id = 'x'; arms[1].modules[0].id = 'x2'","handlingStrategy":"validation","validationCode":"import { collectAllFlowModuleIdsFromModules } from './editableFlowJson'\nconst ids = collectAllFlowModuleIdsFromModules(modules)\nif (new Set(ids).size !== ids.length) throw new Error('Duplicate module IDs (including nested children)')","typeGuard":"function allIdsUnique(modules: FlowModule[]): boolean {\n  const ids = collectAllFlowModuleIdsFromModules(modules)\n  return new Set(ids).size === ids.length\n}","tryCatchPattern":"try {\n  await setFlow({ modules })\n} catch (e) {\n  if (e.message === 'Duplicate module IDs found in flow') {\n    await setFlow(renameDuplicatesDeep(modules)) // rename includes nested branch/loop children\n  } else throw e\n}","preventionTips":["Rename IDs on every copy, including nested children inside branch/for modules","Check ID uniqueness recursively, not just at the top level","Generate IDs from a unique-id helper rather than hand-writing them"],"tags":["validation","duplicate-ids","flow-modules"],"backgroundTag":"duplicate-module-id","analyzedSha":"e474e8803ce2ff5c2df09a58dab51d45f5c922ca","analyzedAt":"2026-09-03T12:38:19.024Z","contentChangedAt":"2026-09-03T12:38:19.024Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}