Skip to content

fix(python3): resolve the worker path without import.meta so the CJS export can run Python - #437

Open
alegal200 wants to merge 2 commits into
vercel-labs:mainfrom
alegal200:fix/python3-cjs-worker-path
Open

alegal200 wants to merge 2 commits into
vercel-labs:mainfrom
alegal200:fix/python3-cjs-worker-path

Conversation

@alegal200

Copy link
Copy Markdown

Problem

python3 fails on every invocation when the library is loaded through its CommonJS export. Node 24.10.0, darwin arm64, pristine just-bash@3.4.2 from npm:

const { Bash } = require("just-bash");
const bash = new Bash({ python: true });
await bash.exec('python3 -c "print(1 + 1)"');
// → { stdout: '', stderr: 'python3: Invalid URL\n', exitCode: 1 }

The ESM export of the same version succeeds. Everything else in the CJS bundle works — echo ok; python3 -c "print(1+1)" prints ok and then fails only on the interpreter — so this reads as a Python problem rather than a packaging one. Reproduced on 3.2.0, 3.3.0 and 3.4.2.

Cause

The worker was located with new URL("./worker.js", import.meta.url). build:lib:cjs runs esbuild with --format=cjs, which replaces import.meta with an empty object, so the base URL is undefined and the constructor throws ERR_INVALID_URL. The minified bundle shows it directly: Bde = {} and Cde = fileURLToPath(new URL("./worker.js", Bde.url)).

Same root cause as #385, which reports it for js-exec. That half appears to be addressed on main by #394js-exec now delegates to run and loads no worker file of its own — while python3 still resolves its worker this way.

Fix

The module directory is read from __dirname when running as CommonJS and from import.meta.url otherwise, then the worker is looked up in the two layouts it ships in:

  1. <dir>/worker.js — source tree, ESM bundle and CLI chunks dirs, non-bundled dist
  2. <dir>/chunks/worker.js — the CJS bundle, where the library is a single dist/bundle/index.cjs and the worker sits one level down

This mirrors sqlite3's existing findWorkerPath(), including the _internals export for tests. A missing worker now names the worker and the build step instead of surfacing as an invalid URL. The failure surface is unchanged otherwise: both before and after, a failure arrives as a python3: command error rather than a crash at load.

Tests

  • python3.worker-resolution.test.ts — resolution order against synthetic directories, modelled on sqlite3.worker-resolution.test.ts.
  • bundle-cjs.test.tsrequire()s the built bundle in a child process and runs python3 -c "print(1 + 1)", added to test:dist. The CJS entry point had no runtime coverage: the suite runs against the source tree, and examples/cjs-consumer only typechecks and only runs echo. That is why this shipped in three releases.

Verified locally: the new CJS test fails with python3: Invalid URL against a build without the fix, and passes with it. test:dist 19/19, test:wasm 715 passed / 2 skipped, typecheck, lint and knip clean. The 7 failures in test:run on my machine (tar xz, symlinked root, hard links) reproduce identically on unmodified main.

One judgement call for you

Locating the worker needs existsSync, and node:fs is a banned import in commands/python3/python3.ts. I used the documented @banned-pattern-ignore opt-out with the worker-bootstrap-gate reason. If you would rather keep the policy symmetric, the alternative is adding python3/python3 next to sqlite3/sqlite3 in the rule's filePattern in scripts/check-banned-patterns.js — happy to switch.

Refs #385

@vercel

vercel Bot commented Sep 18, 2026

Copy link
Copy Markdown
Contributor

@alegal200 is attempting to deploy a commit to the Vercel Labs Team on Vercel.

A member of the Team first needs to authorize it.

@auto-maintain

auto-maintain Bot commented Sep 18, 2026

Copy link
Copy Markdown

🤖 auto-maintain review

Automated, advisory triage for @alegal200's PR. Facts below are read from the GitHub API.

Check Result
Author's merged PRs (this repo) 0 — ⚠️ first-time contributor
Account established ✅ (age 4226d · 13 followers · 17 public repos)
Commits signed/verified ⚠️ 0/1
Changeset included ✅ (.changeset/python3-cjs-worker-path.md)

Review panel: 🟡 medium highest severity

just-bash maintainer code review: 🟢 low

No actionable issues found; the worker resolver covers the shipped CJS and ESM layouts with appropriate regression tests.

General code review: 🟡 medium

The CJS path fix works, but its new missing-worker path incorrectly waits for the full Python timeout.

  • packages/just-bash/src/commands/python3/python3.ts:344 — If worker resolution fails, this catch settles the worker promise but leaves BridgeHandler running. Because executePython starts bridgeHandler.run() afterward and the controller timer is closed, the command hangs for maxPythonTimeoutMs (30s by default), adds a timeout error, and returns exit 124 instead of immediately reporting the missing worker. The failure path must prevent or terminate the bridge run.

Adversarial security: 🟢 low

No actionable adversarial security issues found in the worker-path fix or its tests.

Adversarial security (second opinion): 🟢 low

Small, well-scoped fix: worker path now derives from __dirname/import.meta.url with two candidate layouts that match the actual build outputs, no attacker-controlled input reaches the path, the new throw is sanitized, and there are no dependency, CI, or network changes.

Standard Bash and host portability: 🟢 low

No actionable Bash or host-portability issues found.

Posted by auto-maintain. This automated code review is advisory; a human maintainer makes the call.

This branch has not been deployed

No deployments
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.

1 participant