Skip to content

Zod v3 import codemod turns custom default import names into nonexistent named exports #23301

Closed as not planned

Description

@brennanbutler01

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.mjs
import 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

  1. ai-sdk-factory commented on Oct 14, 2026

    @ai-sdk-factory
    Contributor

    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

    • The codemod rewrites a valid custom-named default import such as import validator from 'zod' into import { validator } from 'zod/v3'.
    • zod/v3 has no named validator export, so the generated code cannot load.
    • The issue includes a reproducible failing assertion and identifies the faulty ImportDefaultSpecifier conversion in both the published and current upstream transform.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions