Skip to content

fix(deploy): upload Build Output modules under their manifest paths - #393

Draft
MattieTK wants to merge 4 commits into
cloudflare:mainfrom
MattieTK:fix/python-vendored-module-names
Draft

MattieTK wants to merge 4 commits into
cloudflare:mainfrom
MattieTK:fix/python-vendored-module-names

Conversation

@MattieTK

@MattieTK MattieTK commented Oct 22, 2026 •

Copy link
Copy Markdown
Member

Fixes #392

What

assembleBuildResult() (packages/cli/src/lib/deploy-input.ts) prefixed every non-entrypoint Build Output module with ./ when turning the manifest into upload modules. This change uses the manifest path as-is, which is what Wrangler uploads.

Why

Python Workers use module names as file paths without normalising them, and import vendored packages from python_modules/. JavaScript module names are normalised, which is why the prefix went unnoticed. With the prefix, pywrangler sync's vendored workers SDK was uploaded as ./python_modules/workers/__init__.py, so cf deploy of any Python Worker that imports the SDK failed validation:

ModuleNotFoundError: No module named 'workers'
You need to update to workers-py >= 1.9 or to pass disable_python_external_sdk [code: 10021]

I captured both tools' uploads against a fake API: the multipart parts are identical in content and content type, and the part names are the only difference (python_modules/... from Wrangler, ./python_modules/... from cf). Full write-up in #392.

The prefix was also inconsistent within cf itself: the main module was already uploaded unprefixed (index.js), while siblings were ./lib.cjs, ./app.py and so on.

Testing

  • Updated the existing assertions in deploy-input.test.ts and commands/deploy/index.test.ts that expected ./-prefixed names.
  • Added keeps Python vendored module paths exactly as in the manifest to deploy-input.test.ts.
  • deploy-input, deploy/index, previews-deploy and workers-check tests pass. workers check runs Workers that import "./message.js" / require("./helper.cjs"), so relative imports still resolve with unprefixed module names.
  • Full run on Linux (WSL2 Ubuntu 22.04, Node 24.22.1):
    • CLI tests: 1948 passed, 1 failed. The failure is cf dev > happy path > reports missing project commands and restores signal listeners, which fails the same way on main (8e51747) in that environment.
    • check:lint, check:format and check:type pass.
  • End to end, against a real account, with the same one-line change applied to the published 1.0.0-beta.20 bundle:
    • Python hello world (vendored workers SDK): failed with 10021 before; now deploys and returns 200 Hello from a Python Worker.
    • Rust hello world (worker-build, Wasm + JS shim): still deploys and returns 200 Hello from a Rust Worker.

Includes a patch changeset.

🤖 Generated with Claude Code

assembleBuildResult() prefixed every non-entrypoint module with "./".
Python Workers only install vendored packages whose module names start
with "python_modules/", so `cf deploy` of a Python Worker importing the
Workers SDK failed with 10021 "No module named 'workers'". Use the
manifest path as-is, matching Wrangler.

Fixes cloudflare#392

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@pkg-pr-new

pkg-pr-new Bot commented Oct 22, 2026 •

Copy link
Copy Markdown
npm i https://pkg.pr.new/cf@393

commit: 461c643

@clanky-the-clanker

Copy link
Copy Markdown

✅ Approved — This is a small, targeted fix that brings non-entrypoint module names in line with the main module and with Wrangler. It comes with a regression test, updated assertions and a patch changeset, and no issues were found.

The decision is advisory: this comment never approves, blocks or requests changes on the pull request.


✅ All checks passed

📋 Code Quality: ✅ LGTM

packages/cli/src/lib/deploy-input.ts:277-285 now uses the manifest module name as-is.

  • Consistent naming: the main module and sourcemaps are already uploaded unprefixed in the same function, so all module names now follow one rule.
  • Path-escape check unchanged: validateModulePath and the filePath resolution still run on the raw manifest name.
  • Nothing else depends on the prefix: no other code in the file builds or matches ./-prefixed names.
  • Comment: the new inline comment says why the name must be left alone (exact python_modules/ matching), which is what a comment here should do.

Tests cover the change. The existing assertions in deploy-input.test.ts and commands/deploy/index.test.ts now expect unprefixed names. The new keeps Python vendored module paths exactly as in the manifest test would fail if the ./ prefix came back.


📝 Documentation: ✅ LGTM

The patch changeset .changeset/python-vendored-module-names.md follows the repository's format: "cf": patch frontmatter, an imperative title and a short body.

The body explains the user-visible symptom (No module named 'workers', code 10021) and the fix (matching Wrangler's module naming). A fix change takes a patch bump, which is the bump used here.


Reviewed commit: 3332567d

@MattieTK
MattieTK marked this pull request as draft October 23, 2026 11:26
Python Workers use module names as file paths without normalising them,
so a `./python_modules/...` package is not on the import path. The
previous text described a prefix-based install step that does not exist,
and named only `cf deploy` although `cf workers versions create` and
`cf previews deploy` share the fix.
Reviews of the Build Output upload names flagged a regression for
service-worker format Workers. That format is deprecated and cf does
not support it, so record this to stop future reviews designing for it.
The comments described a prefix-based install step that does not exist.
The runtime normalises `./` in JavaScript module names but uses Python
module names as file paths, so a `./python_modules/...` package is not
on `sys.path`.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

cf deploy: Python Workers fail with 'No module named workers' because vendored modules are uploaded as ./python_modules/...

2 participants