[Bug]: stagehand example's workersAIClient.ts fails typecheck against regenerated Workers AI types
Nobody has claimed this yet.
Assessment
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Newbie friendliness
- 88/100
- Issue type
- Bug
- Clarity
- Clearly specified
- Activity status
- Active
- Tech stack
- typescript
Research direction
Start with packages/playwright-cloudflare/examples/stagehand/src/worker/workersAIClient.ts and the pinned Wrangler version in package.json. Run npx wrangler types followed by npx tsc --noEmit in the stagehand example, then verify the example typechecks with the regenerated worker-configuration.d.ts and the supported Wrangler versions described in the issue.
Written by the indexing model from the issue text.
Description
Version
packages/playwright-cloudflare/examples/stagehand @ 7c31261.
Steps to reproduce
The example typechecks in this repo only because it commits a worker-configuration.d.ts generated back in September:
Regenerating the types breaks it:
git clone --depth 1 https://github.com/cloudflare/playwright
cd playwright/packages/playwright-cloudflare/examples/stagehand
npm install
npx tsc --noEmit # clean, using the committed worker-configuration.d.ts
npx wrangler types # regenerate against the installed wrangler
npx tsc --noEmit # fails
This matters outside the repo because the Browser Run Stagehand docs tell users to "Copy workersAIClient.ts to your project", where they generate their own types instead of inheriting the committed one.
Expected behavior
The example typechecks against the Workers AI types that its own pinned wrangler generates:
Actual behavior
Two different failures depending on the wrangler version, both originating in the same call:
| wrangler | generated runtime | error |
|---|---|---|
4.57.0 (resolved from the ^4.57.0 above) |
workerd@1.20260103.0 |
TS2322 at messages: Type 'ChatMessage[]' is not assignable to type '{ role: string; content: string; }[]' |
4.125.0 (current) |
workerd@1.20260820.x |
TS2769 at the model argument: Argument of type 'keyof AiModels' is not assignable to parameter of type 'never' |
The root cause is this.modelName as keyof AiModels on line 32. Widening the model id from a literal to the full union means no known-model overload can match, because inputs then has to satisfy every model's input type at once. Resolution falls through to the unknown-model overload, whose Model extends keyof AiModelList ? never : Model constraint is built to reject known keys, which is where the never comes from. That constraint is deliberate and documented in the current generated types:
// Unknown model (fallback).
//
// The `Exclude<..., keyof AiModelList>` constraint forces TypeScript to
// route any model name that is a literal key of `AiModelList` to one of
// the known-model overloads above ...
run<Model extends string>(model: Model extends keyof AiModelList ? never : Model, ...)
That overload does not exist in the committed September types, which is why the problem is invisible here.
The existing // @ts-ignore on line 34 does not help either: it applies to tools on line 35, while the overload-resolution error is reported on the model argument.
Additional context
This patch typechecks cleanly in all three combinations I tried: the committed September types, wrangler 4.57.0 regenerated types, and wrangler 4.125.0 regenerated types. Keeping the literal lets the known-model overload resolve, and one explicit cast covers the genuine mismatch (Stagehand's message and tool shapes are structurally looser than the generated input types) rather than a directive pointed at the wrong line.
-const modelId = "@cf/meta/llama-3.3-70b-instruct-fp8-fast";
+const modelId = "@cf/meta/llama-3.3-70b-instruct-fp8-fast" as const;
+
+type ModelInputs = AiModels[typeof modelId]["inputs"];
@@
- const { response } = await this.binding.run(this.modelName as keyof AiModels, {
+ // Stagehand's message and tool shapes are structurally looser than the
+ // generated Workers AI input types, but the model accepts them at runtime.
+ const inputs = {
messages: options.messages,
- // @ts-ignore
tools: options.tools,
response_format: schema ? {
type: "json_schema",
json_schema: zodToJsonSchema(schema),
} : undefined,
temperature: 0,
- }, this.options) as AiTextGenerationOutput;
+ } as unknown as ModelInputs;
+
+ const { response } = await this.binding.run(modelId, inputs, this.options) as AiTextGenerationOutput;
Happy to open a PR with this if it is useful.
Separately: regenerating worker-configuration.d.ts in CI, or typechecking the examples against current types, would catch this class of drift before it reaches anyone copying the file.
Environment
Node.js v24
npm, Linux x64 (WSL2)
wrangler 4.57.0 (from this example's lockfile) and 4.125.0
typescript 5.8.3 (from this example) and 7.0.2
- Dominant language
- TypeScript
- Stars
- 122
- Forks
- 28
- Avg merge
- 2h 35m
- Merged PRs (30d)
- 1
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from cloudflare/playwright
-
Difficulty 4/5 3-5 days Newbie friendliness 48/100
cloudflare/playwright#220 · 1 comment ·
-
Difficulty 4/5 3-5 days Newbie friendliness 35/100
cloudflare/playwright#93 · 4 comments · 1 reaction ·
-
Difficulty 4/5 3-5 days Newbie friendliness 42/100
cloudflare/playwright#90 · 4 comments · 1 reaction ·
-
Difficulty 4/5 3-5 days Newbie friendliness 35/100
cloudflare/playwright#88 · 1 comment · 1 reaction ·
All issues in cloudflare/playwright
Similar issues
-
calcite-components needs triage refactor
Difficulty 2/5 1-3 hours Newbie friendliness 75/100
Esri/calcite-design-system#15203 ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 91/100
-
community first-timers-only good first issue hacktoberfest help wanted low hanging fruit up-for-grabs
Difficulty 1/5 Under an hour Newbie friendliness 95/100
-
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
Automattic/studio#4908 ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 90/100