windmill-labs/windmill · error

Duplicate module ID found in preprocessor_module: ${preproce

Error message

Duplicate module ID found in preprocessor_module: ${preprocessorModule.id}

What it means

validateEditableFlowJson collects all module IDs in the flow and rejects a preprocessor_module whose id collides with any existing module ID. Module IDs must be unique across the whole flow including special modules; a duplicate would make references (branches, group start/end, failure routing) ambiguous, so the validator throws before saving.

Source

Thrown at frontend/src/lib/components/copilot/chat/flow/editableFlowJson.ts:404

		) {
			throw new Error(
				'Invalid preprocessor_module: only "rawscript" and "script" modules are supported'
			)
		}
	}
	if (failureModule) {
		if (failureModule.id !== SPECIAL_MODULE_IDS.FAILURE) {
			throw new Error(`Invalid failure_module: id must be "${SPECIAL_MODULE_IDS.FAILURE}"`)
		}
		if (failureModule.value.type !== 'rawscript' && failureModule.value.type !== 'script') {
			throw new Error('Invalid failure_module: only "rawscript" and "script" modules are supported')
		}
	}

	const ids = new Set(collectAllFlowModuleIdsFromModules(modules))
	if (preprocessorModule) {
		if (ids.has(preprocessorModule.id)) {
			throw new Error(`Duplicate module ID found in preprocessor_module: ${preprocessorModule.id}`)
		}
		ids.add(preprocessorModule.id)
	}
	if (failureModule && ids.has(failureModule.id)) {
		throw new Error(`Duplicate module ID found in failure_module: ${failureModule.id}`)
	}

	return {
		modules,
		schema,
		preprocessor_module: preprocessorModule,
		failure_module: failureModule,
		groups,
		notes,
		...settings
	}
}

View on GitHub (pinned to e474e8803c)

Solutions

  1. Rename preprocessor_module.id to a fresh unique id (e.g. 'preprocessor')
  2. Rename the colliding module instead if other references point at the preprocessor id
  3. Regenerate the flow JSON letting the tooling assign unique ids

Example fix

// before
{ id: 'step1', value: {...} } // in modules
preprocessor_module: { id: 'step1', value: {...} }
// after
{ id: 'step1', value: {...} }
preprocessor_module: { id: 'preprocessor', value: {...} }
Defensive patterns

Strategy: validation

Validate before calling

const ids = new Set(collectAllFlowModuleIdsFromModules(modules))
if (preprocessor && ids.has(preprocessor.id)) throw new Error('duplicate preprocessor id: ' + preprocessor.id)

Type guard

function hasUniqueIds(modules, preprocessor) {
  const ids = new Set(collectAllFlowModuleIdsFromModules(modules))
  return !preprocessor || !ids.has(preprocessor.id)
}

Try / catch

try {
  const editable = validateEditableFlowJson(json)
} catch (e) {
  if (e.message.includes('Duplicate module ID found in preprocessor_module')) {
    json.preprocessor_module.id = 'preprocessor'
    return validateEditableFlowJson(json)
  }
  throw e
}

Prevention

When it happens

Trigger: Calling validateEditableFlowJson when preprocessorModule.id equals the id of any module already in the modules array (as found by collectAllFlowModuleIdsFromModules), e.g. both are 'pre' or reuse of a main-module id.

Common situations: LLM reusing a placeholder id like 'module1' for the preprocessor; copy-pasting a module into the preprocessor slot without renaming its id; template merging that concatenates two id sets.

Related errors


AI-assisted analysis of windmill-labs/windmill@e474e8803c (2026-09-03). Data as JSON: /api/errors/37c60d9b09eba359. Report an issue: GitHub.