Releases: cloudflare/workers-sdk
@cloudflare/deploy-helpers@0.1.0
Minor Changes
-
#14014
d042705Thanks @emily-shen! - Add@cloudflare/deploy-helperspackage.This introduces a shared internal package for deploy-related helper types and code.
wrangler@4.94.0
Minor Changes
-
#13897
52e9082Thanks @dario-piotrowicz! - Add automatic Cloudflare skills installation for AI coding agentsWrangler now detects AI coding agents and offers to install Cloudflare skill files from the
cloudflare/skillsGitHub repository. Users are prompted once interactively; subsequent runs skip the prompt. Use--install-skillsto install without prompting. -
#13989
f598eacThanks @MattieTK! - Print a QR code alongside the tunnel URL when sharing via Cloudflare TunnelWhen a tunnel is started (via
wrangler dev --tunnelor the Vite plugin withtunnel: true), a scannable QR code is now printed to the terminal beneath the tunnel URL. This makes it easy to open the tunnel on a mobile device without manually copying the URL.The QR code uses Unicode block characters for a compact representation and is generated best-effort -- if generation fails for any reason, the tunnel URL is still displayed as before.
-
#13467
3a1fbedThanks @deloreyj! - Addscheduleproperty to Workflow bindings for cron-based triggeringNote: This is a configuration-only change. Scheduled triggering of Workflow instances is not yet available — adding
scheduleto a Workflow binding will not result in scheduled invocations at this time. This change lays the groundwork for an upcoming feature.Workflow bindings in
wrangler.jsonnow accept an optionalschedulefield that configures one or more cron expressions to automatically trigger new workflow instances on a schedule.// wrangler.json { "workflows": [ { "binding": "MY_WORKFLOW", "name": "my-workflow", "class_name": "MyWorkflow", "schedule": "0 9 * * 1" } ] }
Multiple schedules can be provided as an array:
{ "workflows": [ { "binding": "MY_WORKFLOW", "name": "my-workflow", "class_name": "MyWorkflow", "schedule": ["0 9 * * 1", "0 17 * * 5"] } ] }The schedule is sent to the Workflows control plane on
wrangler deploy. Configuringscheduleon a workflow binding that references an externalscript_nameis an error — the schedule must be configured on the worker that defines the workflow.
Patch Changes
-
#13993
0733688Thanks @dependabot! - Update dependencies of "miniflare", "wrangler"The following dependency versions have been updated:
Dependency From To workerd 1.20260520.1 1.20260521.1 -
#14008
fc1f7b9Thanks @petebacondarwin! - Fix Access Service Token authentication for applications that only allow service tokensWhen using remote bindings against a Worker behind a Cloudflare Access application configured to only allow Service Auth tokens (no interactive user authentication), Wrangler previously ignored the
CLOUDFLARE_ACCESS_CLIENT_IDandCLOUDFLARE_ACCESS_CLIENT_SECRETenvironment variables and the request would fail with a 403.This happened because Wrangler detects Access by looking for a 302 redirect to
cloudflareaccess.com. A service-auth-only Access application has no interactive login path, so it responds with a hard 403 instead of redirecting. Wrangler concluded the domain was not behind Access and skipped attaching the service token headers entirely.The env-var check now runs before the Access detection step, so the configured service token credentials are always used when present.
-
#12277
8c569c6Thanks @penalosa! - Include column names in D1 SQL export INSERT statementsD1 SQL exports now include column names in INSERT statements (e.g.,
INSERT INTO "table" ("col1","col2") VALUES(...)). This ensures that exported SQL can be successfully imported even when the target table has columns in a different order than the original, which commonly occurs during iterative development when schemas evolve. -
Updated dependencies [
0733688,30657e1]:- miniflare@4.20260521.0
miniflare@4.20260521.0
Patch Changes
-
#13993
0733688Thanks @dependabot! - Update dependencies of "miniflare", "wrangler"The following dependency versions have been updated:
Dependency From To workerd 1.20260520.1 1.20260521.1 -
#13999
30657e1Thanks @edmundhung! - Fix TCP requests failing whenoutboundServiceis configuredWorkers using
outboundServicecan now open TCP connections withcloudflare:sockets. Previously, TCP requests could throw an error when a custom outbound service was configured.
@cloudflare/workers-utils@0.21.1
Patch Changes
-
#13933
90092c0Thanks @petebacondarwin! - Mark@cloudflare/workers-utilsas side-effect-free and properly declareundicias a runtime dependencyThe package now declares
"sideEffects": falsein itspackage.jsonso that downstream bundlers can tree-shake unused exports. In particular, consumers that only use a subset of the package (for example,getTodaysCompatDatefrom the main entry) will no longer carry thecloudflared/tunnelexports — or their transitive dependencies — in their final bundle.undicihas been moved fromdevDependenciestodependencies. Previously it was incorrectly listed as a devDependency while the bundler config marked it as external, leaving the publisheddist/index.mjswith an unresolvedimport { fetch } from "undici"for anyone installing the package directly.undiciis deliberately kept external (rather than bundled) so that downstream consumers don't end up with two copies ofundiciin their bundle — which would breakinstanceof Request/Response/Headerschecks across the boundary and preventsetGlobalDispatcher/ proxy configuration from applying to the bundled copy.vitesthas been added as an optionalpeerDependencybecause the./test-helperssub-export usesvitest'svi,beforeEach, andafterEachAPIs at runtime; consumers that import from./test-helpersmust havevitestinstalled themselves.
@cloudflare/vitest-pool-workers@0.16.9
Patch Changes
-
#13933
90092c0Thanks @petebacondarwin! - Derive bundler externals frompackage.jsonand shrink the published bundleThe bundler's
externallist was previously hand-maintained and out of sync withpackage.json—undiciandsemverwere both listed as external despite being onlydevDependencies. The publisheddist/pool/index.mjsconsequently contained a top-levelimport { fetch } from "undici"that was only resolvable because pnpm happened to hoistundicifrom other packages' devDependencies during local development.The bundler now derives its
externallist fromdependencies+peerDependenciesinpackage.json, making it impossible for adevDependencyto silently end up externalized.Combined with the new
"sideEffects": falsedeclaration in@cloudflare/workers-utils, the unusedcloudflared/tunnelexports (and their transitiveundiciimport) are now tree-shaken out of the pool entirely.dist/pool/index.mjsno longer referencesundiciat all, and shrinks from ~489 KB to ~125 KB. -
Updated dependencies [
52e9082,0733688,fc1f7b9,30657e1,8c569c6,f598eac,3a1fbed]:- wrangler@4.94.0
- miniflare@4.20260521.0
@cloudflare/vite-plugin@1.38.0
Minor Changes
-
#13989
f598eacThanks @MattieTK! - Print a QR code alongside the tunnel URL when sharing via Cloudflare TunnelWhen a tunnel is started (via
wrangler dev --tunnelor the Vite plugin withtunnel: true), a scannable QR code is now printed to the terminal beneath the tunnel URL. This makes it easy to open the tunnel on a mobile device without manually copying the URL.The QR code uses Unicode block characters for a compact representation and is generated best-effort -- if generation fails for any reason, the tunnel URL is still displayed as before.
Patch Changes
@cloudflare/pages-shared@0.13.139
@cloudflare/cli-shared-helpers@0.1.4
Patch Changes
- Updated dependencies [
90092c0]:- @cloudflare/workers-utils@0.21.1
wrangler@4.93.1
Patch Changes
-
#13978
fa1f61fThanks @sassyconsultingllc! - Bumpwsfrom 8.18.0 to 8.20.1 to address GHSA-58qx-3vcg-4xpxGHSA-58qx-3vcg-4xpx / CVE-2026-45736 reports an uninitialized-memory disclosure in
ws@<8.20.1when aTypedArrayis passed as the reason argument toWebSocket.close(). The fix shipped in ws@8.20.1 on 2026-05-12. This change bumps the workspace catalog entry so thatminiflare,wrangler, and@cloudflare/vite-pluginall pick up the patched release. -
#13977
2679e05Thanks @dependabot! - Update dependencies of "miniflare", "wrangler"The following dependency versions have been updated:
Dependency From To workerd 1.20260518.1 1.20260519.1 -
#13984
7e40d98Thanks @dependabot! - Update dependencies of "miniflare", "wrangler"The following dependency versions have been updated:
Dependency From To workerd 1.20260519.1 1.20260520.1 -
#13963
adc9221Thanks @gabivlj! - Preserve sibling container image tags during local dev cleanupWrangler now keeps other
cloudflare-devimage tags from the same dev session when multiple containers share a Dockerfile. Previously, duplicate-image cleanup could remove earlier container tags if Docker BuildKit produced the same image ID for each build. -
#13839
735852dThanks @matingathani! - fix: show actionable hint when/membershipsreturns a bad-credentials error (code 9106)Previously,
wranglerthrew a raw Cloudflare API error ("Missing X-Auth-Key, X-Auth-Email or Authorization headers") with no guidance. Now it emits aUserErrorexplaining that an environment variable such asCLOUDFLARE_API_TOKEN,CLOUDFLARE_API_KEY, orCLOUDFLARE_EMAILmay be set to an invalid value, and suggests runningwrangler logout/wrangler loginto re-authenticate. -
#13912
d803737Thanks @petebacondarwin! - Fix/cdn-cgi/*host validation incorrectly accepting subdomains of exact configured routesMiniflare's
/cdn-cgi/*host/origin validator was treating exact configured routes the same as wildcard configured routes, so a request whoseHostorOriginhostname was a subdomain of an exact route (e.g.sub.my-custom-site.comfor amy-custom-site.com/*route) was incorrectly accepted. Exact configured routes and the configuredupstreamhostname are now required to match the request hostname exactly. Subdomain matching is only applied to wildcard routes such as*.example.com/*. Localhost hostnames continue to be allowed as before.This affects
wrangler devand local development through@cloudflare/vite-plugin, both of which use Miniflare under the hood. -
#13919
c7eab7fThanks @petebacondarwin! - Fix the outboundCF-Workerheader reflecting the route pattern hostname instead of the parent zone, and falling back to<worker-name>.example.comundervite dev,vitest-pool-workers, andgetPlatformProxyTwo related issues affected the
CF-Workerheader on outbound subrequests in local development:- Under
@cloudflare/vite-plugin,@cloudflare/vitest-pool-workers, andgetPlatformProxy, the header fell back to<worker-name>.example.comeven whenrouteswere configured, becauseunstable_getMiniflareWorkerOptionsand the equivalentgetPlatformProxyworker-options path did not propagate azonevalue to Miniflare. This broke local development against services that reject unknownCF-Workerhosts (for example, Apple WeatherKit returns403 Forbidden). - Across the above paths and
wrangler dev --local, when a route used thezone_namefield (for example{ pattern: "foo.example.com/*", zone_name: "example.com" }), the header was set to the pattern's hostname (foo.example.com) rather than the zone name (example.com). Production setsCF-Workerto the zone name that owns the Worker, so this was inconsistent with deployed behaviour.
Both bugs are fixed: the new
unstable_getMiniflareWorkerOptions/getPlatformProxypath now propagates azonederived from the first configured route, and all four local-dev paths now prefer a route's explicitzone_nameover the pattern hostname when computing that zone. Whenzone_nameisn't set, the existing best-effort behaviour is preserved — forwrangler devthis meansdev.hostis still honoured as a local override and the pattern hostname is used as a final fallback. Resolving the parent zone forzone_id-only,custom_domain, or plain-string routes would require an API lookup, so locally we still approximate it with the pattern hostname.Note:
dev.hostis intentionally not consulted by theunstable_getMiniflareWorkerOptions/getPlatformProxypaths — thedevconfig block is specific towrangler dev. - Under
-
#13990
e04e180Thanks @petebacondarwin! - Improve the log message shown when an asset upload attempt fails and is retriedThe retry message now reports which attempt is being made (e.g.
Asset upload failed. Retrying... 1 of 5 attempts.), making it easier to gauge how close Wrangler is to exhausting its retry budget. The raw error object is no longer appended to this user-facing message; it is instead logged at debug level (visible viaWRANGLER_LOG=debug). -
#13954
62abf97Thanks @petebacondarwin! - Read the on-disk OAuth state lazily soCLOUDFLARE_API_TOKENfrom.envtakes priority correctlyWrangler previously read its OAuth state from the user auth config file (for example
~/.config/.wrangler/config/default.toml) eagerly at module-import time. That happens before.envfiles are loaded, so the in-memory state would always hold the OAuth tokens even when the user only wanted to authenticate viaCLOUDFLARE_API_TOKEN. If that stored OAuth token happened to be expired, Wrangler would try to refresh it (and fail), aborting the command withFailed to fetch auth token: 400 Bad RequestandNot logged in.— even though a valid API token was in scope.Wrangler now reads the auth config file on demand, after
.envhas been loaded. WhenCLOUDFLARE_API_TOKEN(orCLOUDFLARE_API_KEY+CLOUDFLARE_EMAIL) is present, the OAuth state on disk is no longer consulted, the OAuth refresh endpoint is no longer called, and the env-based token is used directly. Sibling-process refresh-token rotation is also handled naturally because every check reads the current file contents.Internally, the exported
reinitialiseAuthTokens()function is removed — there is no module-level OAuth cache left to invalidate.Fixes #13744.
-
#13951
e349fe0Thanks @sejoker! - Enforce minimum 60 second interval for R2 Data Catalog sinksR2 Data Catalog sinks now require a minimum
--roll-intervalof 60 seconds to prevent compaction issues in the R2 Data Catalog. This validation is applied when creating sinks viawrangler pipelines sinks createwith typer2-data-catalog, and during the interactivewrangler pipelines setupflow.Regular R2 sinks are not affected and can still use intervals as low as 10 seconds.
-
#13959
da0fa8cThanks @dmmulroy! - Recognize Artifacts repositories that are still being createdWrangler's Artifacts repo status type now accepts the
creatinglifecycle state alongside existing in-progress statuses.
miniflare@4.20260520.0
Patch Changes
-
#13978
fa1f61fThanks @sassyconsultingllc! - Bumpwsfrom 8.18.0 to 8.20.1 to address GHSA-58qx-3vcg-4xpxGHSA-58qx-3vcg-4xpx / CVE-2026-45736 reports an uninitialized-memory disclosure in
ws@<8.20.1when aTypedArrayis passed as the reason argument toWebSocket.close(). The fix shipped in ws@8.20.1 on 2026-05-12. This change bumps the workspace catalog entry so thatminiflare,wrangler, and@cloudflare/vite-pluginall pick up the patched release. -
#13977
2679e05Thanks @dependabot! - Update dependencies of "miniflare", "wrangler"The following dependency versions have been updated:
Dependency From To workerd 1.20260518.1 1.20260519.1 -
#13984
7e40d98Thanks @dependabot! - Update dependencies of "miniflare", "wrangler"The following dependency versions have been updated:
Dependency From To workerd 1.20260519.1 1.20260520.1 -
#13912
d803737Thanks @petebacondarwin! - Fix/cdn-cgi/*host validation incorrectly accepting subdomains of exact configured routesMiniflare's
/cdn-cgi/*host/origin validator was treating exact configured routes the same as wildcard configured routes, so a request whoseHostorOriginhostname was a subdomain of an exact route (e.g.sub.my-custom-site.comfor amy-custom-site.com/*route) was incorrectly accepted. Exact configured routes and the configuredupstreamhostname are now required to match the request hostname exactly. Subdomain matching is only applied to wildcard routes such as*.example.com/*. Localhost hostnames continue to be allowed as before.This affects
wrangler devand local development through@cloudflare/vite-plugin, both of which use Miniflare under the hood. -
#13971
59cd880Thanks @petebacondarwin! - Improve error diagnostics in the Browser Run binding workerWhen the local Browser Run binding failed to reach an upstream — for example when Chrome failed to launch and miniflare's loopback
/browser/launchendpoint returned a 500 with a stack-trace text body — the binding worker would callresponse.json()on the non-JSON body and throw an opaqueSyntaxError: Unexpected token X, "..." is not valid JSON. The actual upstream error message (e.g.Chrome readiness probe at ... timed out after 5000ms) was discarded.The binding worker now reads the response body as text first, surfaces the HTTP status and body content in the thrown error, and chains the original
SyntaxErrorviacausewhen the body was a 2xx response that didn't parse as JSON. This makes both local-dev failures and CI test flakes self-diagnosing. -
#13980
e8c2031Thanks @petebacondarwin! - Recover from corrupted@puppeteer/browserscache when launching a Browser Run sessionWhen Miniflare's local Browser Run binding launches Chrome, it calls
@puppeteer/browsers'install()to ensure the binary is present. If a previousinstall()was interrupted mid-extraction (test timeout, process kill, antivirus quarantine), the cache directory can be left partially populated — the folder exists but the executable inside it is missing.install()then throwsThe browser folder (...) exists but the executable (...) is missingon every subsequent call within the same process and the entire test session, breaking every later Browser Run operation until the cache is manually cleared.launchBrowsernow catches that specific error, removes the corrupted cache directory, and retriesinstall()once. If the corruption persists after cleanup, the original error is rethrown with a clearer message.This complements #13971, which surfaced the original error from inside the binding worker. With that diagnostic in place and this self-healing layer, the previously-intermittent "browser folder exists but executable missing" failure mode should no longer fail an entire CI run.