windmill-labs/windmill · error

Invalid AI agent tool name(s): ${invalidToolNames.map((t) =>

Error message

Invalid AI agent tool name(s): ${invalidToolNames.map((t) => `agent "${t.agentId}" tool "${t.toolId}" is named ${JSON.stringify(t.name)} - ${t.error}`).join('; ')}. The tool's "summary" is the name the agent calls it by: use underscores instead of spaces (e.g. "search_docs").

What it means

validateFlowModules rejects AiAgent tools whose `summary` (the name the LLM calls the tool by) contains characters outside ^[a-zA-Z0-9_]+$. The worker enforces this regex at run time, so a flow with a spaced tool name saves fine but fails on every run — this check moves the failure to edit time. The message lists each offending agent id, tool id, current name, and the specific reason.

Source

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

	// Not expressible in the schema: `provider` is required only when the step is standalone, and
	// making AiAgent a conditional union breaks the FlowModuleValue discriminated union it belongs to.
	const providerless = collectProviderlessAgentIds(parsedModules)
	if (providerless.length > 0) {
		throw new Error(
			`AI agent modules ${providerless
				.map((id) => `"${id}"`)
				.join(
					', '
				)} need a provider input transform, or an "agent" path linking them to a saved agent`
		)
	}

	// An agent tool's `summary` is the name the LLM sees; the worker rejects anything outside
	// `^[a-zA-Z0-9_]+$`, so a flow written with a spaced name saves but fails on every run.
	const invalidToolNames = collectInvalidAgentToolNames(parsedModules)
	if (invalidToolNames.length > 0) {
		throw new Error(
			`Invalid AI agent tool name(s): ${invalidToolNames
				.map(
					(t) =>
						`agent "${t.agentId}" tool "${t.toolId}" is named ${JSON.stringify(t.name)} - ${t.error}`
				)
				.join(
					'; '
				)}. The tool's "summary" is the name the agent calls it by: use underscores instead of spaces (e.g. "search_docs").`
		)
	}

	validateAiAgentProviders(parsedModules, ctx.aiProviders, ctx.aiProviderWarnings)

	return parsedModules
}

export function validateFlowSchema(rawSchema: unknown): Record<string, any> | null {
	if (rawSchema == null) return null

View on GitHub (pinned to e474e8803c)

Solutions

  1. Rename each listed tool's `summary` to a snake_case identifier matching ^[a-zA-Z0-9_]+$ (e.g. "search_docs").
  2. Re-run the modules tool after fixing all listed agent/tool pairs.
  3. Keep tool summaries as identifier-style names and put human descriptions in the tool's `description` field instead.

Example fix

// before
{"type":"aiagent","tools":[{"id":"t1","summary":"Search Docs"}]}
// after
{"type":"aiagent","tools":[{"id":"t1","summary":"search_docs"}]}
Defensive patterns

Strategy: validation

Validate before calling

const TOOL_NAME_RE = /^[a-zA-Z0-9_]+$/
const bad = []
for (const m of flow.modules ?? []) {
  if (m?.value?.type !== 'aiagent') continue
  for (const t of m.value.tools ?? []) {
    if (typeof t?.summary === 'string' && !TOOL_NAME_RE.test(t.summary))
      bad.push({agentId: m.id, toolId: t.id, name: t.summary})
  }
}
if (bad.length) throw new Error(`Invalid tool summaries: ${JSON.stringify(bad)}`)

Type guard

function isValidToolName(name) {
  return typeof name === 'string' && /^[a-zA-Z0-9_]+$/.test(name)
}

Try / catch

try {
  const editable = validateEditableFlowJson(rawFlow)
} catch (e) {
  if (String(e.message).startsWith('Invalid AI agent tool name')) {
    // ask the model to rename each listed tool summary to snake_case
  } else throw e
}

Prevention

When it happens

Trigger: Calling flowTools/modules with a flow JSON where an aiagent module's tool has a `summary` like "Search Docs" or contains hyphens, spaces, or unicode instead of snake_case identifiers.

Common situations: An LLM copies a human-readable tool title into `summary`; a user writes tool names with spaces; localized or formatted names are pasted into tool summaries.

Related errors


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