{"record":{"id":"7d94571d68af34c2","repo":"windmill-labs/windmill","slug":"no-endpoint-mapping-found-for-tool-toolname","errorCode":null,"errorMessage":"No endpoint mapping found for tool ${toolName}","messagePattern":"No endpoint mapping found for tool (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"frontend/src/lib/components/copilot/chat/api/apiTools.ts","lineNumber":120,"sourceCode":"\t\tconst endpoint = endpointMap[toolName]\n\t\tconst method = endpoint?.method?.toUpperCase() || 'GET'\n\n\t\t// Determine if tool needs confirmation based on method\n\t\tconst needsConfirmation = ['DELETE', 'POST', 'PUT', 'PATCH'].includes(method)\n\n\t\treturn {\n\t\t\tdef: chatTool,\n\t\t\tplanModeSafe: !!endpoint && ['GET', 'HEAD', 'OPTIONS'].includes(method),\n\t\t\trequiresConfirmation: needsConfirmation,\n\t\t\tconfirmationMessage: `Run ${toolName}`,\n\t\t\tshowDetails: true,\n\t\t\tshowFade: true,\n\t\t\tfn: async ({ args, toolId, toolCallbacks }) => {\n\t\t\t\tconst toolName = chatTool.function.name\n\t\t\t\tconst endpoint = endpointMap[toolName]\n\n\t\t\t\tif (!endpoint) {\n\t\t\t\t\tthrow new Error(`No endpoint mapping found for tool ${toolName}`)\n\t\t\t\t}\n\n\t\t\t\ttry {\n\t\t\t\t\tconst workspace = get(workspaceStore) as string\n\t\t\t\t\tlet path = endpoint.path.replace('{workspace}', workspace)\n\n\t\t\t\t\t// Build URL with path parameters\n\t\t\t\t\tlet url = `/api${path}`\n\t\t\t\t\tconst queryParams: Record<string, string> = {}\n\t\t\t\t\tlet requestBody: any = undefined\n\n\t\t\t\t\t// Process arguments\n\t\t\t\t\tfor (const [key, value] of Object.entries(args)) {\n\t\t\t\t\t\tif (key === 'body') {\n\t\t\t\t\t\t\trequestBody = value\n\t\t\t\t\t\t\tcontinue\n\t\t\t\t\t\t}\n","sourceCodeStart":102,"sourceCodeEnd":138,"githubUrl":"https://github.com/windmill-labs/windmill/blob/e474e8803ce2ff5c2df09a58dab51d45f5c922ca/frontend/src/lib/components/copilot/chat/api/apiTools.ts#L102-L138","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Build tool definitions with buildToolsFromEndpoints(endpointTools) and pass BOTH its tools and endpointMap into createApiTools so keys always match","If constructing manually, add an entry endpointMap[tool.function.name] = { method, path } for every tool passed to createApiTools","Log Object.keys(endpointMap) vs chatTools.map(t => t.function.name) to find the divergent tool name","Assert the tool name against the map before invoking the tool fn (pre-check in the caller)"],"exampleFix":"// before\nconst { tools } = buildToolsFromEndpoints(endpointTools)\nreturn createApiTools(customTools) // endpointMap defaults to {}\n// after\nconst { tools, endpointMap } = buildToolsFromEndpoints(endpointTools)\nreturn createApiTools([...customTools, ...tools], endpointMap)","handlingStrategy":"validation","validationCode":"const missing = chatTools.filter(t => !endpointMap[t.function.name])\nif (missing.length) throw new Error(`Unmapped API tools: ${missing.map(t => t.function.name).join(', ')}`)","typeGuard":"function hasEndpoint(name: string, map: Record<string, { method: string; path: string }>): boolean {\n  return Object.prototype.hasOwnProperty.call(map, name)\n}","tryCatchPattern":"try {\n  await tool.fn({ args, toolId, toolCallbacks })\n} catch (e) {\n  if (e instanceof Error && e.message.startsWith('No endpoint mapping found for tool')) {\n    console.error(`API tool misconfigured: ${e.message}`)\n    return\n  }\n  throw e\n}","preventionTips":["Always derive tools and endpointMap from the same buildToolsFromEndpoints call","Never pass a hand-built chatTools list without a matching endpointMap","Add a unit test asserting every tool name has an endpoint entry"],"tags":["copilot","api-tools","misconfiguration"],"backgroundTag":"missing-endpoint-mapping","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"}