windmill-labs/windmill · error

No endpoint mapping found for tool ${toolName}

Error message

No endpoint mapping found for tool ${toolName}

What it means

createApiTools wraps chat function-calling tools for Windmill API endpoints. Each tool's executable fn looks up its method/path in an endpointMap keyed by tool name; this error is thrown when the fn runs for a tool name with no entry in that map. It is an internal consistency guard: tools are normally built from the same endpointTools list that populates the map, so a miss means the tool list and the map have diverged.

Source

Thrown at frontend/src/lib/components/copilot/chat/api/apiTools.ts:120

		const endpoint = endpointMap[toolName]
		const method = endpoint?.method?.toUpperCase() || 'GET'

		// Determine if tool needs confirmation based on method
		const needsConfirmation = ['DELETE', 'POST', 'PUT', 'PATCH'].includes(method)

		return {
			def: chatTool,
			planModeSafe: !!endpoint && ['GET', 'HEAD', 'OPTIONS'].includes(method),
			requiresConfirmation: needsConfirmation,
			confirmationMessage: `Run ${toolName}`,
			showDetails: true,
			showFade: true,
			fn: async ({ args, toolId, toolCallbacks }) => {
				const toolName = chatTool.function.name
				const endpoint = endpointMap[toolName]

				if (!endpoint) {
					throw new Error(`No endpoint mapping found for tool ${toolName}`)
				}

				try {
					const workspace = get(workspaceStore) as string
					let path = endpoint.path.replace('{workspace}', workspace)

					// Build URL with path parameters
					let url = `/api${path}`
					const queryParams: Record<string, string> = {}
					let requestBody: any = undefined

					// Process arguments
					for (const [key, value] of Object.entries(args)) {
						if (key === 'body') {
							requestBody = value
							continue
						}

View on GitHub (pinned to e474e8803c)

Solutions

  1. Build tool definitions with buildToolsFromEndpoints(endpointTools) and pass BOTH its tools and endpointMap into createApiTools so keys always match
  2. If constructing manually, add an entry endpointMap[tool.function.name] = { method, path } for every tool passed to createApiTools
  3. Log Object.keys(endpointMap) vs chatTools.map(t => t.function.name) to find the divergent tool name
  4. Assert the tool name against the map before invoking the tool fn (pre-check in the caller)

Example fix

// before
const { tools } = buildToolsFromEndpoints(endpointTools)
return createApiTools(customTools) // endpointMap defaults to {}
// after
const { tools, endpointMap } = buildToolsFromEndpoints(endpointTools)
return createApiTools([...customTools, ...tools], endpointMap)
Defensive patterns

Strategy: validation

Validate before calling

const missing = chatTools.filter(t => !endpointMap[t.function.name])
if (missing.length) throw new Error(`Unmapped API tools: ${missing.map(t => t.function.name).join(', ')}`)

Type guard

function hasEndpoint(name: string, map: Record<string, { method: string; path: string }>): boolean {
  return Object.prototype.hasOwnProperty.call(map, name)
}

Try / catch

try {
  await tool.fn({ args, toolId, toolCallbacks })
} catch (e) {
  if (e instanceof Error && e.message.startsWith('No endpoint mapping found for tool')) {
    console.error(`API tool misconfigured: ${e.message}`)
    return
  }
  throw e
}

Prevention

When it happens

Trigger: Calling createApiTools(chatTools) with hand-built chatTools (or with the default endpointMap={}) so the returned tool fn executes for a name that was never registered via buildToolsFromEndpoints; also if a tool name is renamed/mutated after map construction.

Common situations: Developers wiring custom API tools into the copilot pass tool definitions without the matching endpoint map; loading API tools from a stale config where tool list and map were generated from different endpoint snapshots; tests constructing tools directly with an empty default map.

Related errors


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