windmill-labs/windmill · error

Duplicate module ID found in failure_module: ${failureModule

Error message

Duplicate module ID found in failure_module: ${failureModule.id}

What it means

validateEditableFlowJson rejects a failure_module whose id already exists among the flow's module IDs (including the preprocessor, which is added to the id set just before this check). IDs must be globally unique in a flow; duplicates would corrupt module references, so the error is thrown before anything is saved.

Source

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

	}
	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
	}
}

/**
 * Build the agent-facing compact view of a flow. When an `InlineScriptSession`
 * is provided, every rawscript module's `content` is moved into the session
 * and replaced with the placeholder `inline_script.<moduleId>` (preprocessor
 * and failure modules included).

View on GitHub (pinned to e474e8803c)

Solutions

  1. Set failure_module.id to the reserved special id (SPECIAL_MODULE_IDS.FAILURE, i.e. 'failure') only if not already taken; otherwise pick a unique id
  2. Rename the colliding main-module id instead if references depend on it
  3. Regenerate with unique ids via the copilot tooling

Example fix

// before
modules: [{ id: 'failure', value: {...} }]
failure_module: { id: 'failure', ... }
// after
modules: [{ id: 'step1', value: {...} }]
failure_module: { id: 'failure', value: { type: 'rawscript', ... } }
Defensive patterns

Strategy: validation

Validate before calling

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

Type guard

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

Try / catch

try {
  const editable = validateEditableFlowJson(json)
} catch (e) {
  if (e.message.includes('Duplicate module ID found in failure_module')) {
    delete json.failure_module // or rename the colliding module id
    return validateEditableFlowJson(json)
  }
  throw e
}

Prevention

When it happens

Trigger: Calling validateEditableFlowJson when failureModule.id matches any id in the modules array or the preprocessor_module id, e.g. two modules both id'd 'failure' or reusing 'last_step'.

Common situations: LLM emitting failure_module with id 'failure' while a main module already uses that id; copying an existing module into failure_module without changing its id; manual JSON edits merging flows.

Related errors


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