{"record":{"id":"8261e9fb0b6a9d4d","repo":"mastra-ai/mastra","slug":"code-mode-external-identifier-collision-tools","errorCode":null,"errorMessage":"Code Mode external identifier collision: tools \"${existing}\" and \"${toolId}\" both map to external_${externalName}","messagePattern":"Code Mode external identifier collision: tools \"(.+?)\" and \"(.+?)\" both map to external_(.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/core/src/tools/code-mode/runner.ts","lineNumber":60,"sourceCode":"/**\n * Produce the full runner source to write into the sandbox and run with node.\n */\nexport function buildRunner({ programModule, externals }: BuildRunnerOptions): string {\n  // `buildRunner` is exported, so a caller could pass a non-sanitized name.\n  // External names become global property suffixes, so reject anything that\n  // isn't a legal identifier instead of producing an unusable global.\n  const SAFE_IDENT = /^[A-Za-z_$][A-Za-z0-9_$]*$/;\n  const seen = new Map<string, string>();\n  for (const { externalName, toolId } of externals) {\n    if (!SAFE_IDENT.test(externalName)) {\n      throw new Error(`Invalid Code Mode external identifier: ${externalName}`);\n    }\n    // Two tool ids can sanitize to the same external name (e.g. `a-b` and\n    // `a_b` both become `a_b`). The install loop below would silently overwrite\n    // the earlier global, leaving one tool unreachable. Fail fast instead.\n    const existing = seen.get(externalName);\n    if (existing) {\n      throw new Error(\n        `Code Mode external identifier collision: tools \"${existing}\" and \"${toolId}\" both map to external_${externalName}`,\n      );\n    }\n    seen.set(externalName, toolId);\n  }\n\n  // Externals are emitted as JSON data, not interpolated identifiers. The\n  // runner installs each `external_<name>` global in a loop using bracket\n  // assignment, so no caller-derived string is ever spliced into the generated\n  // source as code. This keeps tool ids strictly data, even if `sanitize`\n  // changes.\n  const externalsJson = JSON.stringify(externals.map(({ externalName, toolId }) => ({ externalName, toolId })));\n\n  return `'use strict';\nconst FRAME_PREFIX = ${JSON.stringify(FRAME_PREFIX)};\n\nfunction __emit(frame) {\n  process.stdout.write(FRAME_PREFIX + JSON.stringify(frame) + '\\\\n');","sourceCodeStart":42,"sourceCodeEnd":78,"githubUrl":"https://github.com/mastra-ai/mastra/blob/75dd419e613fe9c39f846ffc500716141b74fda6/packages/core/src/tools/code-mode/runner.ts#L42-L78","documentation":"When building the runner, two distinct tool ids can sanitize to the same external name (e.g. 'a-b' and 'a_b' both become 'a_b'), which would make the second global overwrite the first and leave one tool unreachable. buildRunner fails fast with this collision error instead.","triggerScenarios":"Registering two tools in createCodeMode whose ids differ only by characters sanitized to underscores, e.g. 'get-data' and 'get_data'.","commonSituations":"Teams mixing kebab-case and snake_case tool names; importing tools from multiple packages with overlapping names; programmatically generated ids that differ only in punctuation.","solutions":["Rename one of the colliding tool ids so their sanitized external names differ.","Adopt a single naming convention (e.g. camelCase) for tool ids used with Code Mode.","Pre-check sanitized names for duplicates before passing the tool map to createCodeMode."],"exampleFix":"// before\ncreateCodeMode({ tools: { 'get-user': a, 'get_user': b } });\n// after\ncreateCodeMode({ tools: { 'getUser': a, 'get_user_v2': b } });","handlingStrategy":"validation","validationCode":"const seen = new Map<string, string>();\nfor (const id of Object.keys(tools)) {\n  const ext = sanitizeToolId(id);\n  if (seen.has(ext)) throw new Error(`Collision: \"${seen.get(ext)}\" and \"${id}\" -> external_${ext}`);\n  seen.set(ext, id);\n}","typeGuard":null,"tryCatchPattern":"try {\n  codeMode = createCodeMode({ tools });\n} catch (e) {\n  if (String(e.message).includes('external identifier collision')) {\n    // rename one of the tools named in the message and retry\n  } else throw e;\n}","preventionTips":["Use one naming convention (camelCase) for all tool ids.","Run a dedupe check on sanitized names when merging tool maps.","Avoid kebab/snake-case pairs that differ only in punctuation."],"tags":["code-mode","identifier-collision","naming","validation"],"backgroundTag":"identifier-collision","analyzedSha":"75dd419e613fe9c39f846ffc500716141b74fda6","analyzedAt":"2026-08-30T00:15:31.844Z","schemaVersion":2},"datasetVersion":"2026-08-30T03:17:51.788Z"}