en un clic
pkg-debug
// Debug and troubleshoot @yao-pkg/pkg packaging issues — build failures, runtime crashes, missing assets, bloated binaries, native addon errors, cross-compile regressions, SEA sentinel problems, and patches/dictionaries.
// Debug and troubleshoot @yao-pkg/pkg packaging issues — build failures, runtime crashes, missing assets, bloated binaries, native addon errors, cross-compile regressions, SEA sentinel problems, and patches/dictionaries.
Cross-compile test harness for @yao-pkg/pkg. Builds a tiny hello.js for every (mode × target) combination and runs what can be executed on a Linux host (native x64, arm64 via docker+qemu, win-x64 via docker-wine). Use when the user wants to verify pkg cross-compilation claims, reproduce issues #87/#181, sanity-check a pkg PR, or compare Standard vs Enhanced SEA across Node 20/22/24. Trigger: "test cross-compile", "run pkg matrix", "verify xcompile", or /pkg-xcompile-test.
Run packaging performance benchmarks comparing Standard PKG vs SEA modes (with/without bundling) on a Node.js project. Default target is zwave-js-ui. Measures build time, binary size, and startup time.
| name | pkg-debug |
| description | Debug and troubleshoot @yao-pkg/pkg packaging issues — build failures, runtime crashes, missing assets, bloated binaries, native addon errors, cross-compile regressions, SEA sentinel problems, and patches/dictionaries. |
| when_to_use | "debug pkg", "pkg not working", "packaged binary crashes", "missing file in binary", "binary too large", "cross-compile broken", "native addon fails", "SEA error", "Multiple occurences of sentinel", "ERR_REQUIRE_ESM", "Cannot find module" in pkg context, "Cannot execute binary from snapshot" |
| user-invocable | true |
| disable-model-invocation | false |
| argument-hint | ["description of the issue"] |
| allowed-tools | Read Bash Grep Glob Agent |
| effort | high |
Diagnose and fix issues when packaging Node.js apps with @yao-pkg/pkg.
User's issue: $ARGUMENTS
Official docs: https://yao-pkg.github.io/pkg/
For the full pkg configuration schema, patches examples, dictionary format, and contributing guide, see reference.md.
Identify the failure stage:
| Stage | Symptom | Jump to |
|---|---|---|
| Build | pkg exits with error before producing a binary | Build failures |
| Runtime | Binary produced but crashes/errors on launch | Runtime errors |
| Missing files | App runs but can't find assets/modules at runtime | Missing assets |
| Binary size | Binary is unexpectedly large | Binary bloat |
| Cross-compile | Build OK on host but binary broken on target | Cross-compile |
| Native addons | .node files fail to load | Native addons |
| SEA-specific | Enhanced SEA sentinel/blob/fuse errors | SEA issues |
pkg --debug app.js -o dist/app # Inject diagnostic prelude (build-time)
pkg . --sea # Enhanced SEA (stock Node.js, no bytecode)
pkg . --no-bytecode # Standard mode without V8 bytecode
pkg . --options max-old-space-size=4096 # Bake Node.js flags into binary
These work inside the packaged binary, not at build time.
| Variable | Values | Purpose |
|---|---|---|
DEBUG_PKG | 1 | Dump VFS tree + symlink table at startup (needs --debug build) |
DEBUG_PKG | 2 | Above + trace every fs call (readFile, stat, readdir, ...) |
DEBUG_PKG_PERF | 1 | Startup performance report (SEA only, works without --debug) |
SIZE_LIMIT_PKG | bytes | With DEBUG_PKG, only show files larger than N (default 5 MB) |
FOLDER_LIMIT_PKG | bytes | With DEBUG_PKG, only show folders larger than N (default 10 MB) |
PKG_NATIVE_CACHE_PATH | path | Override native addon extraction directory (default ~/.cache/pkg-native/) |
# 1. Build with diagnostic prelude
pkg --debug . -o dist/app
# 2. List everything in the VFS
DEBUG_PKG=1 ./dist/app
# 3. Search for a specific file
DEBUG_PKG=1 ./dist/app 2>&1 | grep "myfile.json"
# 4. Trace all fs calls at runtime
DEBUG_PKG=2 ./dist/app
# 5. SEA startup profiling (no --debug needed)
DEBUG_PKG_PERF=1 ./dist/app
ERR_REQUIRE_ESMESM module loaded via require().
pkg app.js --options experimental-require-module
Not needed on Node >= 22.12.0 (flag is default). See: https://yao-pkg.github.io/pkg/guide/troubleshooting#error-err-require-esm
Multiple occurences of sentinel (SEA)@yao-pkg/pkg is in the project's dependencies — the walker bundles it
into the SEA archive and the sentinel string causes postject to fail.
Fix: move @yao-pkg/pkg to devDependencies, or upgrade to pkg >= 6.16.0.
Node 22 V8 bytecode is architecture-specific.
# Option 1: Use SEA (no bytecode step)
pkg . --sea -t node22-linux-arm64
# Option 2: Skip bytecode
pkg . --no-bytecode --public-packages '*' --public -t node22-linux-arm64
# Option 3: Target Node 24 (regression fixed)
pkg . -t node24-linux-arm64
Cannot find module XXX (child_process)Child process re-enters the packaged binary. Unset PKG_EXECPATH:
spawn(process.execPath, [...], {
env: { ...process.env, PKG_EXECPATH: '' }
});
Cannot execute binary from snapshotOS can't exec binaries from VFS. Extract to disk first:
if (process.pkg) {
const { pipeline } = require('stream/promises');
const file = fs.createWriteStream('ffmpeg');
await pipeline(fs.createReadStream('/snapshot/path/ffmpeg'), file);
fs.chmodSync('ffmpeg', 0o755);
}
.node native addons are auto-extracted. Other binaries must be extracted manually.
See: https://yao-pkg.github.io/pkg/guide/troubleshooting#error-cannot-execute-binary-from-snapshot
ERR_INSPECTOR_NOT_AVAILABLE / internalModuleStat is not a functionNODE_OPTIONS leaking from IDE/shell. Clear them:
unset NODE_OPTIONS NODE_DEBUG NODE_EXTRA_CA_CERTS NODE_NO_WARNINGS
./app
See: https://yao-pkg.github.io/pkg/guide/troubleshooting#error-err-inspector-not-available
require(variable) — walker can't statically resolve itpath.join with variable — path.join(__dirname, var) not detectedpkg auto-detects path.join(__dirname, 'literal.ext') only when:
See: https://yao-pkg.github.io/pkg/guide/detecting-assets
{
"pkg": {
"scripts": ["build/**/*.js"],
"assets": ["views/**/*", "templates/**/*.html", "config/*.json"]
}
}
scripts — JS files compiled to V8 bytecode (source stripped).assets — raw files embedded as-is, accessible under /snapshot/.pkg --debug . -o dist/app
DEBUG_PKG=1 ./dist/app 2>&1 | grep "expected-file"
If the file is missing, add it to assets. If it's a JS file loaded dynamically, add to scripts.
pkg --debug . -o dist/app
SIZE_LIMIT_PKG=500000 FOLDER_LIMIT_PKG=2000000 DEBUG_PKG=1 ./dist/app
ignore{
"pkg": {
"ignore": [
"**/*/node_modules/*/test/**",
"**/*/node_modules/*/docs/**",
"**/*/node_modules/*/.github/**"
]
}
}
Note: ** and * do NOT match dotfiles — spell .github explicitly.
Pre-bundle with esbuild/webpack, then pkg --sea bundle.js. This produces
the smallest SEA binaries because only reachable code is included.
See: https://yao-pkg.github.io/pkg/guide/advanced-debug-vfs
Standard cross-compile is broken on Node 22:
linux-arm64 → runtime crash Error: UNEXPECTED-20 (#181)win-x64 → silent exit code 4, no output (#87)Workarounds: (any one)
pkg . --sea (Enhanced SEA — works out of the box)pkg . --no-bytecode --public-packages '*' --public (skip bytecode)SEA cross-compile works when host major == target major. When they differ, pkg must execute the downloaded target binary to generate the blob — this fails for cross-platform builds (Linux host can't run macOS binary).
Rule: match host Node major to target Node major. Use nvm use <major>
before running pkg.
Native .node files are auto-extracted to ~/.cache/pkg-native/<sha256>/.
linuxstatic target cannot load native addons — use linux target.node must be compiled for target archPKG_NATIVE_CACHE_PATH.node file — add to assets: "node_modules/pkg/prebuilds/**/*.node"See: https://yao-pkg.github.io/pkg/guide/native-addons
| Input | Mode |
|---|---|
pkg app.js --sea | Simple SEA (single bundled file) |
pkg . --sea (with package.json) | Enhanced SEA (full walker + VFS) |
pkg . (no --sea) | Standard mode (V8 bytecode, patched Node.js) |
DEBUG_PKG_PERF=1 output interpretation[pkg:perf] manifest parse 14.0ms ← JSON parsing overhead
[pkg:perf] archive load 1.2ms ← Raw binary load
[pkg:perf] vfs mount + hooks 3.3ms ← fs patching
[pkg:perf] module loading 730.1ms ← require() chain
High module loading → too many files; consider pre-bundling with esbuild.
SEA auto-patches the Worker constructor for /snapshot/ paths. If a worker
fails to find its module, verify with DEBUG_PKG=1 that the file is in the VFS.
See: https://yao-pkg.github.io/pkg/guide/sea-mode
When a dependency uses code patterns that don't work in packaged apps (e.g.
require('inspector'), hardcoded paths, dynamic imports), use patches in
your package.json pkg config. pkg also ships built-in dictionary/
entries for known packages that are applied automatically.
For the full patches format, real-world examples, monorepo path conventions, dictionary file structure, and contributing guide, see reference.md.
When a user reports any pkg issue, run through this:
--sea flag or pkg.sea: true)node --version, target triple)pkg succeed? Does the binary run?DEBUG_PKG=1 — is the expected file in the VFS?NODE_OPTIONS clean? — printenv | grep NODErm -rf ~/.pkg-cache && pkg ...inspector,
dynamic imports, or hardcoded paths → see reference.md.node files match target arch