{"record":{"id":"f5b8938b1a592bf4","repo":"can1357/oh-my-pi","slug":"oh-my-pi-pkg-version-tarball-contained-no","errorCode":null,"errorMessage":"@oh-my-pi/${pkg}@${version} tarball contained no .node files","messagePattern":"@oh-my-pi/(.+?)@(.+?) tarball contained no \\.node files","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/metaharness/src/tb/agent.ts","lineNumber":44,"sourceCode":" * leaf is the artifact real installs load, so it is the ground truth for\n * embedding; an unpublished working-tree version fails instead of risking\n * loader/API skew.\n */\nasync function refreshCrossNatives(arches: GuestArch[], version: string): Promise<void> {\n\tfor (const arch of arches) {\n\t\tif (process.platform === \"linux\" && process.arch === arch) continue;\n\t\tconst pkg = `pi-natives-linux-${arch}`;\n\t\tconst response = await fetch(`https://registry.npmjs.org/@oh-my-pi/${pkg}/-/${pkg}-${version}.tgz`);\n\t\tif (!response.ok) throw new Error(`Fetching @oh-my-pi/${pkg}@${version} failed: HTTP ${response.status}`);\n\t\tconst archive = new Bun.Archive(await response.arrayBuffer());\n\t\tlet extracted = 0;\n\t\tfor (const [entry, file] of await archive.files()) {\n\t\t\tconst name = path.posix.basename(entry);\n\t\t\tif (!name.startsWith(`pi_natives.linux-${arch}`) || !name.endsWith(\".node\")) continue;\n\t\t\tawait Bun.write(path.join(NATIVES_DIR, name), file);\n\t\t\textracted++;\n\t\t}\n\t\tif (extracted === 0) throw new Error(`@oh-my-pi/${pkg}@${version} tarball contained no .node files`);\n\t}\n}\n\n/** Build and cache self-contained omp binaries for the selected guest architectures. */\nexport async function prepareAgentBinaries(opts: {\n\tarches: GuestArch[];\n\tcacheDir: string;\n\trebuild: boolean;\n}): Promise<AgentBinaries> {\n\tconst manifest = (await Bun.file(path.join(CODING_AGENT_DIR, \"package.json\")).json()) as { version?: unknown };\n\tif (typeof manifest.version !== \"string\" || manifest.version.length === 0) {\n\t\tthrow new Error(\"packages/coding-agent/package.json has no valid version\");\n\t}\n\n\tconst arches = [...new Set(opts.arches)];\n\tconst cached: Partial<Record<GuestArch, string>> = {};\n\tfor (const arch of arches) {\n\t\tcached[arch] = path.join(opts.cacheDir, `omp-linux-${arch}-${manifest.version}`);","sourceCodeStart":26,"sourceCodeEnd":62,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/metaharness/src/tb/agent.ts#L26-L62","documentation":"After extracting a cross-target natives tarball, refreshCrossNatives counts files matching `pi_natives.linux-<arch>*.node`; if none matched it throws, meaning the published tarball is malformed or does not ship natives for the requested architecture. This guards against silently proceeding with no cross natives bundled.","triggerScenarios":"The downloaded `@oh-my-pi/pi-natives-linux-<arch>` tarball contained zero `.node` files whose basename starts with `pi_natives.linux-<arch>` — e.g. the package published an empty/placeholder tarball, or the archive layout changed so `.node` files sit elsewhere or under different names.","commonSituations":"A newly published platform package version shipped without built binaries; npm served a stale/cached empty tarball; native file naming convention changed upstream so the `startsWith` filter no longer matches; wrong arch requested for a package that does not build that target.","solutions":["Inspect the tarball contents (`tar tzf <pkg>-<version>.tgz`) to see what was actually published","Republish the platform package with correctly built .node binaries","If the naming convention changed, update the filter in refreshCrossNatives to match the new names","Verify you requested a supported GuestArch that the package actually ships"],"exampleFix":"// before\nawait refreshCrossNatives([\"riscv64\"], version); // package doesn't ship riscv64 natives\n// after\nconst supported: GuestArch[] = [\"x64\", \"arm64\"];\nawait refreshCrossNatives(arches.filter(a => supported.includes(a)), version);","handlingStrategy":"validation","validationCode":"// inspect the tarball before relying on it\nconst res = await fetch(tarballUrl);\nconst archive = new Bun.Archive(await res.arrayBuffer());\nconst hasNatives = (await archive.files()).some(([e]) =>\n  path.posix.basename(e).startsWith(`pi_natives.linux-${arch}`) && e.endsWith(\".node\"));\nif (!hasNatives) throw new Error(`${pkg}@${version} ships no ${arch} natives`);","typeGuard":null,"tryCatchPattern":"try {\n  await refreshCrossNatives(arches, version);\n} catch (err) {\n  if (err instanceof Error && err.message.includes(\"tarball contained no .node files\")) {\n    throw new Error(`bad publish of @oh-my-pi/${pkg}@${version}; pin a known-good version`);\n  }\n  throw err;\n}","preventionTips":["Pin known-good versions of the platform packages","Inspect tarball contents after each upstream publish","Keep the .node naming filter in sync with the package's file layout","Only request arches the package actually ships"],"tags":["npm","artifacts","validation"],"backgroundTag":"empty-package-artifact","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}