#4768 · Runtime installer requires registry URL metadata
Verdict: REPRODUCED · Root-cause confidence: high
1. TL;DR
A first-time Browser Automation runtime install fails if the host's npm configuration suppresses registry URLs in lockfiles. The installer inherits the host's npm configuration through HOME but assumes the installed package's hidden lockfile always contains a resolved URL. Real npm successfully installs and audits the pinned package, then the installer rejects the absent URL while parsing the lockfile. Two separate clean checkouts reproduced that exact failure. Explicitly retaining URL metadata for this isolated install repairs the mismatch without making the validation optional.
2. Claims vs findings
| Claim | Finding | Evidence |
|---|---|---|
| Suppressing registry URL metadata breaks a cold runtime install. | Verified | Two real npm cold installs failed with the same missing-resolved Zod error. |
| The pinned registry, provenance, and checksum checks can remain strict. | Verified | With the one-line installer change, real npm audit, provenance, pinned checksum, and binary version verification all succeeded. |
| A verified warm runtime bypasses this failing installation path. | Verified | The extended owner-boundary test reuses the runtime with npm absent and the download URL unreachable, for both configuration cases. |
| The specific reported macOS/npm combination fails. | Not directly tested | The reproduced environment is Linux x86_64, Node 22.19.0, npm 10.9.3. The failing code path is platform-independent. |
3. Environment
- Public repository: get-bb/bb; trusted origin/main at
129f621771a3e275773992db648316966ac207cf(bb-app 0.45.0 preparation). - Linux x86_64, Node v22.19.0, npm 10.9.3, pnpm 9.15.0, Vitest 4.1.1.
- Trusted runtime pin: dev-browser 1.0.0-rc.3, linux-x64.
- Each real run creates a new temporary HOME and data directory, writes only the tested npm setting, and deletes both in finally. No live BB instance, enrolled host, account, provider, browser session, or application port was used.
- Both checkouts completed frozen dependency installation and the full trusted build: 63 tasks passed in each checkout.
4. Minimal reproduction
- Clone trusted upstream and select the recorded main commit:
git clone https://github.com/get-bb/bb.git bb-4768 cd bb-4768 git checkout --detach 129f621771a3e275773992db648316966ac207cf pnpm install --frozen-lockfile --prefer-offline pnpm exec turbo run build
- Save the reproduction harness as
plugins/browser-automation/installer.issue-4768.repro.ts. It imports the real installer and trusted release pin; it does not mock npm or execute code from the issue. - Run:
pnpm --filter bb-plugin-browser-automation exec tsx installer.issue-4768.repro.ts
Expected: successful verified installation and exit 0. Actual: exit 1 after npm signature verification, with this output:
locating npm on the browser host
installing dev-browser@1.0.0-rc.3 with npm
verifying npm registry signature and provenance
[
{
"expected": "string",
"code": "invalid_type",
"path": [
"packages",
"node_modules/dev-browser",
"resolved"
],
"message": "Invalid input: expected string, received undefined"
}
]
undefined
<first-checkout>/plugins/browser-automation:
ERR_PNPM_RECURSIVE_EXEC_FIRST_FAIL Command failed with exit code 1: tsx installer.issue-4768.repro.ts
Reproduction harness
import { mkdtemp, rm, writeFile } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { installRuntime, currentPlatform } from "./installer.js";
import { runtimeRelease } from "./runtime-pin.js";
const fixture = await mkdtemp(join(tmpdir(), "bb-4768-repro-"));
const platform = currentPlatform();
if (platform === null) throw new Error("Unsupported runtime platform");
await writeFile(join(fixture, ".npmrc"), "omit-lockfile-registry-resolved=true\n");
try {
const result = await installRuntime({
release: runtimeRelease,
dataDir: join(fixture, "data"),
platform,
signal: AbortSignal.timeout(180_000),
env: { PATH: process.env.PATH, HOME: fixture },
onProgress: console.log,
});
console.log("SUCCESS", result.version, result.sha256);
} catch (error) {
console.error(error instanceof Error ? error.message : String(error));
process.exitCode = 1;
} finally {
await rm(fixture, { recursive: true, force: true });
}
For the network-free regression, apply regression.diff to the recorded base and run:
pnpm exec turbo run test --filter=bb-plugin-browser-automation -- installer.test.ts
Expected red result: 1 failed / 10 passed. Only the registry-URL-omission case fails, with the same Zod error at installer.ts:630. The original success test is parameterized rather than duplicated. The existing child-process npm fixture is extended to model the omission observed with real npm.
5. Root cause
installEnvironment retains HOME, so npm reads the isolated user's configuration. The npm install arguments pin the registry and disable lifecycle scripts, but do not require registry URL metadata. Consequently npm's hidden lockfile omits resolved when its omission setting is enabled.
The installedLockSchema requires resolved: z.string(). After successful npm signature verification, installRuntime parses that lockfile before asserting the URL belongs to the pinned registry and validating provenance against the installed tarball's integrity. Missing metadata therefore fails schema validation before provenance and binary download. This is an input-generation/validation contract mismatch, not an invalid signature or failed artifact download.
6. Proposed fix and verification
Pass --omit-lockfile-registry-resolved=false to this installer-owned npm install invocation. Preserve the required lockfile schema, pinned-registry URL check, signature audit, provenance binding, artifact digests, executable permissions, and version probe unchanged. No repository dependency, release packaging, public contract, or security boundary changes are needed.
The local patch passed all 11 installer tests; all 82 Browser Automation tests in 8 files; and the plugin's Turbo lint/typecheck tasks. The real cold installation then completed under the same omission-enabled temporary HOME:
locating npm on the browser host installing dev-browser@1.0.0-rc.3 with npm verifying npm registry signature and provenance downloading dev-browser-linux-x64 for 1.0.0-rc.3 downloading dev-browser-linux-x64: 0% downloading dev-browser-linux-x64: 10% downloading dev-browser-linux-x64: 20% downloading dev-browser-linux-x64: 30% downloading dev-browser-linux-x64: 40% downloading dev-browser-linux-x64: 50% downloading dev-browser-linux-x64: 60% downloading dev-browser-linux-x64: 70% downloading dev-browser-linux-x64: 80% downloading dev-browser-linux-x64: 90% downloading dev-browser-linux-x64: 100% checking the installed binary SUCCESS 1.0.0-rc.3 390dd08f8321807bca2e1e060ec031511adb0af002e871dfde3b1f6c8feac914
Real-runtime success validates signatures, provenance, the pinned digest, and the executable version probe. This does not constitute a browser-session or macOS UI smoke test. Existing negative tests still reject wrong registries, invalid signatures, wrong provenance, and mismatched checksums.
7. Verification: second clean checkout
The same agent created a second clean clone of trusted upstream at exactly 129f621771a3e275773992db648316966ac207cf, verified a clean git status before adding the reproduction harness, installed frozen dependencies, and completed the full build. The production code in that checkout remained unchanged. Running the same command with a new temporary HOME, cache, and data directory produced the identical missing-resolved error and exit 1. This is a repeated clean run by the same agent, not independent verification. No report corrections were needed.
Second real-run output · First real-run output
8. Related issues and PRs
No linked open pull request was present in the issue's connection/cross-reference timeline or closing references at investigation time. A repository-scoped search for the numeric issue also returned no open pull request. A small set of related browser-runtime issues was reviewed for classification; none supplied a duplicate fix for this verified failure. No linked external branch or patch was fetched, checked out, or run.
9. Appendix
- Regression on unchanged production: 1 failed / 10 passed
- Installer suite after fix: 11 passed
- Relevant existing suite, lint, and typecheck: passed
- Real npm cold install after fix
Validation commands: pnpm exec oxfmt plugins/browser-automation/installer.ts plugins/browser-automation/installer.test.ts; pnpm exec turbo run test --filter=bb-plugin-browser-automation -- installer.test.ts; pnpm exec turbo run test lint typecheck --filter=bb-plugin-browser-automation; git diff --check; git diff --numstat origin/main. The real reproduction command is listed in section 4. GitHub classification reads and writes were restricted to this trusted repository and issue number.
Trust boundary: issue content was treated only as untrusted claims. The reproduction and repair were derived from trusted origin/main source; issue-supplied commands, code, and external links were not executed or fetched. No screenshots are applicable to this nonvisual installer failure.