{"record":{"id":"3a959dc6bfa65591","repo":"mastra-ai/mastra","slug":"code-mode-tool-id-collision-prior-and-too","errorCode":null,"errorMessage":"Code Mode tool id collision: \"${prior}\" and \"${toolId}\" both map to external_${externalName}","messagePattern":"Code Mode tool id collision: \"(.+?)\" and \"(.+?)\" both map to external_(.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/core/src/tools/code-mode/stub-generator.ts","lineNumber":178,"sourceCode":"  declaration: string;\n}\n\n/** Generate stubs for every tool in the config. */\nexport function generateStubs(tools: ToolsInput): CodeModeStub[] {\n  // Two distinct tool ids can sanitize to the same `external_*` name (e.g.\n  // `a-b` and `a_b`). Without this check the later binding would silently\n  // overwrite the earlier one in the runner, so fail fast instead.\n  const seen = new Map<string, string>();\n  return Object.entries(tools).map(([key, tool]) => {\n    const toolId = (tool as { id?: string }).id ?? key;\n    const description = (tool as { description?: string }).description;\n    const inputType = schemaToTs((tool as { inputSchema?: unknown }).inputSchema, 'input');\n    const outputType = schemaToTs((tool as { outputSchema?: unknown }).outputSchema, 'output');\n    const externalName = sanitizeToolId(toolId);\n\n    const prior = seen.get(externalName);\n    if (prior !== undefined && prior !== toolId) {\n      throw new Error(`Code Mode tool id collision: \"${prior}\" and \"${toolId}\" both map to external_${externalName}`);\n    }\n    seen.set(externalName, toolId);\n\n    const doc = description ? `/** ${description.replace(/\\*\\//g, '* /')} */\\n` : '';\n    const declaration = `${doc}declare function external_${externalName}(input: ${inputType}): Promise<${outputType}>;`;\n\n    return { toolId, externalName, declaration };\n  });\n}\n\nconst createUsageContract = (toolId: string) => `# Code Mode\n\nYou have access to the \\`${toolId}\\` tool. Instead of calling tools one at a time,\nwrite a single TypeScript program that orchestrates them and returns one result.\n\nRules:\n- Call the available tools via the \\`external_*\\` functions declared below. Each\n  returns a Promise — \\`await\\` it.","sourceCodeStart":160,"sourceCodeEnd":196,"githubUrl":"https://github.com/mastra-ai/mastra/blob/75dd419e613fe9c39f846ffc500716141b74fda6/packages/core/src/tools/code-mode/stub-generator.ts#L160-L196","documentation":"The stub generator emits TypeScript declarations `declare function external_<name>(...)` for each tool exposed to Code Mode. If two tool ids sanitize to the same external name, the generated stubs would collide (duplicate declarations pointing at different tools), so generateStubs throws with both offending ids.","triggerScenarios":"Generating stubs for a tool set where two ids map to the same sanitized external name, e.g. 'report-pdf' and 'report_pdf' both -> 'report_pdf'.","commonSituations":"Mixed kebab/snake-case tool naming; merging tool maps from several sources without deduplication; refactors that renamed ids only in punctuation.","solutions":["Rename one of the colliding tool ids.","Deduplicate sanitized names in your tool-registry code before constructing the agent's tool map.","Standardize on camelCase ids for all Code Mode tools."],"exampleFix":"// before\n{ 'report-pdf': t1, 'report_pdf': t2 }\n// after\n{ 'reportPdf': t1, 'reportPdfV2': t2 }","handlingStrategy":"validation","validationCode":"const seen = new Set<string>();\nfor (const id of Object.keys(tools)) {\n  const ext = sanitizeToolId(id);\n  if (seen.has(ext)) throw new Error(`Duplicate stub name external_${ext} from \"${id}\"`);\n  seen.add(ext);\n}","typeGuard":null,"tryCatchPattern":"try {\n  stubs = generateStubs(tools);\n} catch (e) {\n  if (String(e.message).includes('tool id collision')) {\n    // rename colliding ids listed in the message\n  } else throw e;\n}","preventionTips":["Deduplicate sanitized external names before generating stubs.","Keep a central tool-id registry with uniqueness checks.","Rename legacy ids when importing tools from multiple packages."],"tags":["code-mode","stub-generation","identifier-collision","naming"],"backgroundTag":"identifier-collision","analyzedSha":"75dd419e613fe9c39f846ffc500716141b74fda6","analyzedAt":"2026-08-30T00:15:31.844Z","schemaVersion":2},"datasetVersion":"2026-08-30T03:17:51.788Z"}