pkg Debugging & Troubleshooting
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.
Triage — Start here
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 |
Diagnostic tools
CLI flags
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
Runtime environment variables
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/) |
Standard diagnostic workflow
# 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
Build failures
ERR_REQUIRE_ESM
ESM 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.
Bytecode compilation fails for cross-arch
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
Runtime errors
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 snapshot
OS 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 function
NODE_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
Missing assets or modules
Why files go missing
- Dynamic
require(variable)— walker can't statically resolve it - Non-JS assets (templates, JSON, images) — not followed by walker
path.joinwith variable —path.join(__dirname, var)not detected
Auto-detection rules
pkg auto-detects path.join(__dirname, 'literal.ext') only when:
- Exactly 2 arguments
- Second argument is a string literal (not a variable, not a template with expressions)
See: https://yao-pkg.github.io/pkg/guide/detecting-assets
Manual configuration (package.json)
{
"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/.
Verification
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.
Binary bloat
Diagnose
pkg --debug . -o dist/app
SIZE_LIMIT_PKG=500000 FOLDER_LIMIT_PKG=2000000 DEBUG_PKG=1 ./dist/app
Fix with ignore
{
"pkg": {
"ignore": [
"**/*/node_modules/*/test/**",
"**/*/node_modules/*/docs/**",
"**/*/node_modules/*/.github/**"
]
}
}
Note: ** and * do NOT match dotfiles — spell .github explicitly.
Consider SEA with bundler
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
Cross-compile issues
Node 22 Standard mode regression
Standard cross-compile is broken on Node 22:
linux-arm64→ runtime crashError: 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)- Target Node 20 or Node 24 (regression is Node-22-specific)
Cross-platform SEA builds
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 addon issues
Native .node files are auto-extracted to ~/.cache/pkg-native/<sha256>/.
Common problems
linuxstatictarget cannot load native addons — uselinuxtarget- Arch mismatch —
.nodemust be compiled for target arch - Cache deleted — antivirus may clean cache dir. Set
PKG_NATIVE_CACHE_PATH - Missing
.nodefile — add toassets:"node_modules/pkg/prebuilds/**/*.node"
See: https://yao-pkg.github.io/pkg/guide/native-addons
SEA-specific issues
Mode selection
| 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.
Worker threads in SEA
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
Patches and dictionaries
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.
Quick checklist
When a user reports any pkg issue, run through this:
- What mode? Standard or SEA? (
--seaflag orpkg.sea: true) - What Node version? Host and target (
node --version, target triple) - Build or runtime failure? Does
pkgsucceed? Does the binary run? DEBUG_PKG=1— is the expected file in the VFS?NODE_OPTIONSclean? —printenv | grep NODE- Stale cache? —
rm -rf ~/.pkg-cache && pkg ... - Patches needed? — check if the failing module uses
inspector, dynamic imports, or hardcoded paths → see reference.md - Native addons? — verify
.nodefiles match target arch - Cross-compile? — Node 22 Standard mode is broken; use SEA or Node 24
- Bug in pkg itself? — see contributing guide