{"record":{"id":"741fd8bf368dcb95","repo":"mastra-ai/mastra","slug":"invalid-code-mode-external-identifier-externaln","errorCode":null,"errorMessage":"Invalid Code Mode external identifier: ${externalName}","messagePattern":"Invalid Code Mode external identifier: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/core/src/tools/code-mode/runner.ts","lineNumber":53,"sourceCode":" * Top-level `return`, `await`, and `const` work because the body lives inside\n * an async function.\n */\nexport function buildProgramModule(program: string): string {\n  return `export default async function () {\\n${program}\\n}\\n`;\n}\n\n/**\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.","sourceCodeStart":35,"sourceCodeEnd":71,"githubUrl":"https://github.com/mastra-ai/mastra/blob/75dd419e613fe9c39f846ffc500716141b74fda6/packages/core/src/tools/code-mode/runner.ts#L35-L71","documentation":"Code Mode exposes each tool to sandboxed code as a global named external_<sanitizedToolId>. buildRunner validates each sanitized external name against /^[A-Za-z_$][A-Za-z0-9_$]*$/ because it becomes a global property suffix; an invalid identifier would produce unusable code, so buildRunner throws.","triggerScenarios":"A tool id sanitizes (via sanitizeToolId) to a string that is not a legal JS identifier, e.g. starts with a digit ('123-tool' -> '123_tool') or is empty/whitespace after sanitization.","commonSituations":"Tool names beginning with numbers or composed only of punctuation; dynamically generated tool ids from user input; non-ASCII tool ids stripped to an empty string by sanitization.","solutions":["Rename the tool id so it sanitizes to a legal identifier (start with a letter, $, or _).","If tool ids come from user input, validate them against a safe-identifier pattern at registration time.","Prefix numeric-leading ids (e.g. 'tool-123' instead of '123')."],"exampleFix":"// before\ncreateCodeMode({ tools: { '123-weather': weatherTool } });\n// after\ncreateCodeMode({ tools: { 'weather123': weatherTool } });","handlingStrategy":"validation","validationCode":"const SAFE_IDENT = /^[A-Za-z_$][A-Za-z0-9_$]*$/;\nfor (const id of Object.keys(tools)) {\n  if (!SAFE_IDENT.test(sanitizeToolId(id))) {\n    throw new Error(`Tool id \"${id}\" is not Code Mode-safe; rename it`);\n  }\n}","typeGuard":"function isCodeModeSafeId(id: string): boolean {\n  return /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(id) && id.length > 0;\n}","tryCatchPattern":"try {\n  codeMode = createCodeMode({ tools });\n} catch (e) {\n  if (String(e.message).includes('Invalid Code Mode external identifier')) {\n    // sanitize/rename offending tool ids programmatically\n  } else throw e;\n}","preventionTips":["Name tools with leading letters (never digits or punctuation).","Validate tool ids at registration time, not at Code Mode build time.","Reject user-supplied tool ids that fail identifier checks."],"tags":["code-mode","identifier","naming","validation"],"backgroundTag":"invalid-identifier","analyzedSha":"75dd419e613fe9c39f846ffc500716141b74fda6","analyzedAt":"2026-08-30T00:15:31.844Z","schemaVersion":2},"datasetVersion":"2026-08-30T03:17:51.788Z"}