mastra-ai/mastra · error
Code Mode external identifier collision: tools "${existing}"
Error message
Code Mode external identifier collision: tools "${existing}" and "${toolId}" both map to external_${externalName} What it means
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.
Source
Thrown at packages/core/src/tools/code-mode/runner.ts:60
/**
* Produce the full runner source to write into the sandbox and run with node.
*/
export function buildRunner({ programModule, externals }: BuildRunnerOptions): string {
// `buildRunner` is exported, so a caller could pass a non-sanitized name.
// External names become global property suffixes, so reject anything that
// isn't a legal identifier instead of producing an unusable global.
const SAFE_IDENT = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
const seen = new Map<string, string>();
for (const { externalName, toolId } of externals) {
if (!SAFE_IDENT.test(externalName)) {
throw new Error(`Invalid Code Mode external identifier: ${externalName}`);
}
// Two tool ids can sanitize to the same external name (e.g. `a-b` and
// `a_b` both become `a_b`). The install loop below would silently overwrite
// the earlier global, leaving one tool unreachable. Fail fast instead.
const existing = seen.get(externalName);
if (existing) {
throw new Error(
`Code Mode external identifier collision: tools "${existing}" and "${toolId}" both map to external_${externalName}`,
);
}
seen.set(externalName, toolId);
}
// Externals are emitted as JSON data, not interpolated identifiers. The
// runner installs each `external_<name>` global in a loop using bracket
// assignment, so no caller-derived string is ever spliced into the generated
// source as code. This keeps tool ids strictly data, even if `sanitize`
// changes.
const externalsJson = JSON.stringify(externals.map(({ externalName, toolId }) => ({ externalName, toolId })));
return `'use strict';
const FRAME_PREFIX = ${JSON.stringify(FRAME_PREFIX)};
function __emit(frame) {
process.stdout.write(FRAME_PREFIX + JSON.stringify(frame) + '\\n');View on GitHub (pinned to 75dd419e61)
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.
Example fix
// before
createCodeMode({ tools: { 'get-user': a, 'get_user': b } });
// after
createCodeMode({ tools: { 'getUser': a, 'get_user_v2': b } }); Defensive patterns
Strategy: validation
Validate before calling
const seen = new Map<string, string>();
for (const id of Object.keys(tools)) {
const ext = sanitizeToolId(id);
if (seen.has(ext)) throw new Error(`Collision: "${seen.get(ext)}" and "${id}" -> external_${ext}`);
seen.set(ext, id);
} Try / catch
try {
codeMode = createCodeMode({ tools });
} catch (e) {
if (String(e.message).includes('external identifier collision')) {
// rename one of the tools named in the message and retry
} else throw e;
} Prevention
- 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.
When it happens
Trigger: Registering two tools in createCodeMode whose ids differ only by characters sanitized to underscores, e.g. 'get-data' and 'get_data'.
Common situations: 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.
Related errors
- Invalid Code Mode external identifier: ${externalName}
- Code Mode tool id collision: "${prior}" and "${toolId}" both
- Project name must be 1-214 lowercase characters, start with
- Invalid skill name "${name}". Names must start with alphanum
- ClaudeSDKAgent resumeData must include sessionId or continue
AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30).
Data as JSON: /api/errors/8261e9fb0b6a9d4d.
Report an issue: GitHub.