Repository navigation
Zod v3 import codemod turns custom default import names into nonexistent named exports #23301
Description
Description
The v5/replace-zod-import-with-v3 transform breaks a valid default import when its local name is not z. For example, import validator from zod becomes a named validator import from zod/v3, which has no such export. The application can no longer load the migrated schema module.
Reproduction
Save as repro.mjs in a new directory. This uses synthetic fixtures and mocked providers; no credentials or live API calls are needed.
npm init -y
npm install @ai-sdk/codemod@4.0.3 jscodeshift@17.3.0 esbuild@0.28.2 zod@4.6.7
node repro.mjsimport assert from 'node:assert/strict';
import { build } from 'esbuild';
import jscodeshift from 'jscodeshift';
import { createRequire } from 'node:module';
import { dirname, resolve } from 'node:path';
const require = createRequire(import.meta.url);
const root = process.env.CODEMOD_ROOT ?? dirname(require.resolve('@ai-sdk/codemod/package.json'));
const j = jscodeshift.withParser('tsx');
async function load(name) {
const entry = resolve(root, process.env.CODEMOD_ROOT ? `src/codemods/${name}.ts` : `dist/codemods/${name}.js`);
const built = await build({ entryPoints: [entry], bundle: true, write: false, format: 'esm', platform: 'node' });
const { default: transform } = await import('data:text/javascript;base64,' + Buffer.from(built.outputFiles[0].text).toString('base64'));
return source => transform({ path: 'fixture.ts', source }, { jscodeshift: j, j, report() {} }, {}) ?? source;
}
const transform=await load('v5/replace-zod-import-with-v3');
const input="import validator from 'zod'; export const schema = validator.object({name:validator.string()});";
const output=transform(input);console.log(output);
const original=await import('zod');
assert.equal(original.default.object({name:original.default.string()}).parse({name:'Example'}).name,'Example');
const migrated=await import('zod/v3');
const specifier=j(output).find(j.ImportDeclaration).nodes()[0].specifiers[0];
const importedName=specifier.type==='ImportDefaultSpecifier'?'default':specifier.imported.name;
console.log({importedName,exportPresent:importedName in migrated});
assert.ok(importedName in migrated,'The migrated default import must resolve to an existing Zod export');Both the published transform and current upstream emit import { validator } from zod/v3. The reproduction first verifies that the original default import can create and parse a schema, then confirms that the generated named import does not exist in the actual zod/v3 module. The final assertion fails.
AI SDK Version
Node.js v26.7.0; @ai-sdk/codemod@4.0.3 jscodeshift@17.3.0 esbuild@0.28.2 zod@4.6.7 (latest relevant release checked) and locally built upstream 6c65131f106f0ffcf77d0a3b685f67cbc43ea74b.
Root cause and proposed fix
Source. Every ImportDefaultSpecifier is converted to an ImportSpecifier using its local identifier as the imported export name. Default import local names are arbitrary; they do not identify a named export in the source package.
Untested proposal: preserve the default import while changing the module path, or generate import { z as validator } using the original local name. Cover default imports named z, validator, and schemaLib, including mixed named imports. Validate the generated imports against the actual module exports. PR #8206 introduced the transform and describes default imports named z; the current matcher also rewrites every other default name.
Code of Conduct
- I agree to follow this project's Code of Conduct.
Activity
ai-sdk-factory commentedon Oct 14, 2026
Identify issue type
Identified as Bug
Confidence: High
Rationale
This is a concrete failure in an existing migration codemod: it generates invalid imports from valid input. The proposed fix does not change the primary classification from bug.
Evidence
import validator from 'zod'intoimport { validator } from 'zod/v3'.zod/v3has no namedvalidatorexport, so the generated code cannot load.ImportDefaultSpecifierconversion in both the published and current upstream transform.