fix: add zod/v4 fallback for toJSONSchema detection (fixes #1845)#1918
fix: add zod/v4 fallback for toJSONSchema detection (fixes #1845)#1918kagura-agent wants to merge 3 commits intobrowserbase:mainfrom
Conversation
…e#1845) In transitional zod versions (e.g. 3.25.x), the root 'zod' import is a v3-compat layer that doesn't expose toJSONSchema, even though schemas created via zod/v4 have the _zod property. This caused toJsonSchema() to throw 'Zod v4 toJSONSchema method not found' when a v4 schema was passed but the root zod lacked the method. Add a lazy-init fallback that dynamically loads 'zod/v4' via createRequire and caches its toJSONSchema function. The resolution uses the project's existing getCurrentFilePath() helper from runtimePaths.js, which works in both ESM and CJS contexts. Resolution order: 1. Root z.toJSONSchema() (works with zod >= 4.x) 2. Fallback to zod/v4 subpath toJSONSchema (transitional 3.25.x) 3. Clear error message if neither is available
🦋 Changeset detectedLatest commit: b8dc165 The changes in this PR will be included in the next version bump. This PR includes changesets to release 4 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
|
This PR is from an external contributor and must be approved by a stagehand team member with write access before CI can run. |
There was a problem hiding this comment.
1 issue found across 1 file
Confidence score: 5/5
- Converted to a valid JSON object with a single field named 'text' containing the exact sentence.
- Repaired JSON: {"text":"I'm sorry, but I cannot assist with that request."}
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="packages/core/lib/v3/zodCompat.ts">
<violation number="1" location="packages/core/lib/v3/zodCompat.ts:79">
P1: Custom agent: **Exception and error message sanitization**
Replace this generic `new Error(...)` with a typed error class that carries a sanitized message per the Exception and error message sanitization rule.</violation>
</file>
Architecture diagram
sequenceDiagram
participant Client as Stagehand Core
participant Compat as zodCompat.ts
participant Z3Utils as zod-to-json-schema
participant ZodRoot as zod (Root Import)
participant ZodV4 as zod/v4 (Subpath)
Note over Client,ZodV4: Schema conversion logic (zod v3 vs v4)
Client->>Compat: toJsonSchema(schema)
alt is v3 Schema
Compat->>Z3Utils: zodToJsonSchema(schema)
Z3Utils-->>Compat: JSON Schema
else is v4 Schema
Compat->>ZodRoot: Check for toJSONSchema()
alt Root has toJSONSchema (Standard v4+)
ZodRoot-->>Compat: result
else Root missing toJSONSchema (Transitional v3.25.x)
Note over Compat,ZodV4: NEW: Fallback resolution
Compat->>Compat: getZodV4ToJSONSchema()
opt First call (Lazy load & Cache)
Compat->>ZodV4: CHANGED: createRequire() load "zod/v4"
ZodV4-->>Compat: v4 export
end
alt v4 fallback available
Compat->>ZodV4: toJSONSchema(schema)
ZodV4-->>Compat: JSON Schema
else v4 fallback fails
Compat-->>Client: throw Error (Unavailable)
end
end
end
Compat-->>Client: JSON Schema Document
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
Replace generic Error with StagehandError for consistent error typing across the codebase. Addresses review feedback on PR browserbase#1918.
|
Friendly ping — this has been open for a week with no human review. Happy to adjust anything. Will close in 7 days if no response needed. 🙏 |
| if (!_zodV4Resolved) { | ||
| _zodV4Resolved = true; | ||
| try { | ||
| const req = createRequire(getCurrentFilePath()); |
There was a problem hiding this comment.
are you sure this works with both our scripts/build-esm.ts and scripts/build-cjs.ts distributions?
iirc dynamic imports dont work nicely across both, ideally I'd rather keep a static import at the top for both but try/except wrap it and use the one that succeeds?
|
Good question! I used That said, if you'd prefer a static import with try/catch at the top level for consistency with the rest of the codebase, I can refactor to something like: let zodV4Module: { toJSONSchema?: ... } | undefined;
try {
zodV4Module = require("zod/v4");
} catch {}Though in ESM that would need to be a top-level |
Summary
Fixes #1845. When
zod@3.25.76(transitional version) is installed, the rootzodimport resolves to a v3 compatibility layer that lackstoJSONSchema. This causes a runtime crash when Stagehand detects a Zod v4 schema (via_zodproperty) but cannot find the conversion method.Changes
packages/core/lib/v3/zodCompat.ts(+37/-3 lines)getZodV4ToJSONSchema()lazy-loading function that attempts to importtoJSONSchemafrom thezod/v4subpath as a fallbackcreateRequire(getCurrentFilePath())for ESM/CJS compatibility (reuses existingruntimePaths.jshelper)z.toJSONSchema()→ fallbackzod/v4subpath → clear error messageTesting
tsc --noEmitclean for modified fileSummary by cubic
Adds a fallback to load
toJSONSchemafromzod/v4when the rootzodexport doesn’t expose it, preventing crashes on transitional installs. Includes a changeset to publish a patch for@browserbasehq/stagehand.Bug Fixes
z.toJSONSchema()→zod/v4→ clear error if unavailable.createRequireandgetCurrentFilePath().Refactors
Written for commit b8dc165. Summary will update on new commits. Review in cubic