08 runtime diagnostics
Skill FluxonLab/Skillry/plugins/runtime-and-local-app/skills/08-runtime-diagnostics
Installable, permission-bounded, multi-platform agent skills & subagents for Claude Code, Codex, Copilot & Gemini/Antigravity — 125 skills + 73 subagents across 18 departments, with a validation harness, native plugin marketplace, and full upstream attribution. by FluxonLab.
npx -y skills add FluxonLab/Skillry --skill 08-runtime-diagnosticsAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
2 things to look at
- no licenseNo license file was found in the repository. Code published without one is not open source by default, so using it at work is a question for whoever answers licensing questions where you are.
- 2 stars2 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
Use when you need to diagnose startup, process, port, dependency, environment, and runtime health failures.
SKILL.md
11.4 KB, as published. Nobody here has run it
Runtime Diagnostics
Purpose
Provide a systematic, non-destructive procedure for diagnosing why a local or dev-environment application fails to start, crashes immediately, or behaves incorrectly at runtime. It covers port conflicts, missing or wrong environment variables, runtime/dependency version mismatches, wrong working directory, broken native modules, and process-level resource limits — the failure classes behind most "it works on my machine" reports. The output is, per step, the command run, the finding, the action taken, and the verification that the issue is resolved.
When to use
npm run dev/python manage.py runserver/go run .exits non-zero and the cause is not obvious.- A service starts but immediately crashes (OOM, unhandled exception, fatal signal).
- "Address already in use" /
EADDRINUSEon a specific port. - The app runs but cannot reach its database, cache, or an external dependency.
- A container or process starts on one machine but not another due to environment differences.
When not to use
- The error is a compile/type error — that is a build problem; fix the build, not the runtime.
- The app is in production and you need live incident triage — use the observability stack, not local diagnostics.
- The failure is a logic bug inside a healthy, running process — use a debugger or targeted logging.
Procedure
- Capture the exact startup error, untruncated. The first non-warning line is almost always the root cause. Run the start command and tee full stderr:
npm run dev 2>&1 | tee /tmp/startup.log. - Resolve port conflicts. If the error mentions
EADDRINUSE: find the holder withlsof -i :PORT(macOS/Linux) ornetstat -ano | findstr :PORT(Windows), confirm the PID belongs to a stale dev server (ps -p PID -o pid,command), then kill it and confirm the port frees. - Verify the runtime version matches the project. Compare
node --versionagainst.nvmrc/engines.node;python --versionagainst.python-version/pyproject.toml;go versionagainst thegodirective ingo.mod. Switch with nvm/pyenv before continuing on a mismatch. - Check environment variable shape. Read
.env.examplefor required keys, then confirm each is set and non-empty without printing values. Validate plausibility (aDATABASE_URLthat starts withpostgres://). - Verify dependency install is complete and correct. Node:
node_modulespresent and in sync with the lockfile (npm ci). Python: the venv is active (which pythonpoints inside it) andpip checkis clean. Go:go mod verify. - Check filesystem preconditions. Some apps need
uploads/,tmp/,logs/, orcerts/to exist with correct permissions at startup. Confirm withls -la. - Trace external dependency connectivity directly. Ping the DB (
psql "$DATABASE_URL" -c '\conninfo',redis-cli -u "$REDIS_URL" ping) and HTTP deps (curl -s -o /dev/null -w '%{http_code}\n' "$API/health"). Report the actual response, not "it worked." - Inspect the process table for zombies or duplicates.
ps aux | grep -E 'node|python|ruby|java'— look for two instances on the same port, or a process that should be dead. - Check system resources.
df -h(a full disk causes opaque write failures),free -m/vm_stat(memory), andulimit -n(macOS defaults to 256 file descriptors, which connection-heavy Node apps exhaust). - Decode the exit signal if the process died immediately: 137 = OOM/SIGKILL, 139 = SIGSEGV (often a native module), 143 = SIGTERM, 130 = Ctrl-C. On Linux, check
dmesgfor OOM-killer evidence. - Confirm the loaded config and CWD when the app starts but behaves as if unconfigured: print
process.cwd()/os.getcwd()and verify the.envactually read is the one you edited.
Concrete checks
- Full untruncated stderr from startup captured.
- Port conflict checked with
lsof -i :PORT; no stale process holds the port. - Runtime version matches
.nvmrc/.python-version/go.mod. - All required env vars present and non-empty, compared against
.env.example. - Dependencies installed cleanly (
npm ci/pip check/go mod verify). - Required directories exist with correct permissions.
- Database connectivity confirmed with a direct client ping.
- External HTTP dependencies reachable from the dev machine.
- No zombie or duplicate instances of the process running.
- Disk space and file-descriptor limits within safe bounds.
- The process exit code/signal is decoded (137 OOM, 139 segfault, 143 SIGTERM) rather than guessed.
- The working directory and the actually-loaded config file are confirmed, not assumed.
- Native modules are rebuilt for the runtime that will execute them (container vs host parity).
- The bound port is confirmed with
lsof, not taken from the startup log line.
Commands
# 1. Capture the full startup error
npm run dev 2>&1 | tee /tmp/startup.log | head -50
# 2. Port conflict: find, identify, free
lsof -i :3000 # macOS/Linux (Windows: netstat -ano | findstr :3000)
ps -p "$(lsof -ti :3000)" -o pid,command # confirm it is a stale dev server
kill "$(lsof -ti :3000)" && lsof -i :3000 || echo "port 3000 free"
# 3. Runtime version vs project requirement
node --version; cat .nvmrc 2>/dev/null
python --version; cat .python-version 2>/dev/null
go version; grep '^go ' go.mod 2>/dev/null
# 4. Env shape WITHOUT printing values
for k in $(grep -oE '^[A-Z0-9_]+' .env.example 2>/dev/null); do
[ -n "${!k:-}" ] && echo "$k set" || echo "$k MISSING"
done
# 5. Dependency integrity
npm ci --dry-run 2>&1 | tail -5 # Node lockfile in sync?
pip check # Python broken requirements
go mod verify # Go module checksums
# 7. External connectivity
psql "$DATABASE_URL" -c '\conninfo' 2>&1 | head -1
redis-cli -u "$REDIS_URL" ping 2>/dev/null
curl -s -o /dev/null -w 'http=%{http_code} t=%{time_total}s\n' "$API_BASE/health"
# 9. Resource limits
df -h . ; ulimit -n ; (free -m 2>/dev/null || vm_stat | head -5)
# Decode the exit signal of a process that died immediately
"$@"; echo "exit=$?" # 137=OOM/SIGKILL, 139=SIGSEGV, 143=SIGTERM, 130=Ctrl-C
dmesg 2>/dev/null | rg -i "killed process|out of memory" | tail -5 # Linux OOM-killer evidence
# Attach to a crashing Node process to see the real stack
node --stack-trace-limit=50 --unhandled-rejections=strict dist/server.js 2>&1 | head -40
# Confirm which config file the app actually loaded (wrong-CWD trap)
node -e 'console.log(process.cwd())' # where it thinks it is
rg -n "dotenv|load_dotenv|godotenv" src/ | head # how/where env is loaded
# Container parity: rebuild native modules for the runtime that will actually run them
npm rebuild bcrypt sharp 2>&1 | tail -5
# Watch a backgrounded service settle (replaces a blind sleep)
until curl -fsS http://localhost:3000/health >/dev/null 2>&1; do sleep 0.5; done; echo "ready"
# Trace a "cannot find module" / version-mismatch to the exact package
npm ls <pkg> 2>&1 | rg "<pkg>|deduped|invalid" # resolved version + conflicts
node -e 'console.log(require.resolve("<pkg>"))' # which copy is actually loaded
pip show <pkg> | rg "Version|Location" # Python: installed version + path
# File-descriptor leak: count open FDs for the process over time
PID=$(lsof -ti :3000); ls /proc/$PID/fd 2>/dev/null | wc -l # Linux
lsof -p "$PID" | wc -l # macOS
# Verify the runtime inside a container matches the host expectation
docker run --rm -v "$PWD":/app -w /app node:20 node --version
docker run --rm -v "$PWD":/app -w /app node:20 npm ci && echo "installs clean in target image"
Symptom to cause to command
| Symptom | Likely cause | First command |
|---|---|---|
EADDRINUSE | stale dev server on the port | lsof -i :PORT |
| exits with code 137 | OOM kill | dmesg | rg -i 'out of memory' |
| exits with code 139 | native module segfault | npm rebuild <native-pkg> |
| "cannot find module X" | install out of sync with lockfile | npm ci |
| connects nowhere, no error | wrong working directory / .env not loaded | node -e 'console.log(process.cwd())' |
ECONNREFUSED to DB | DB down or wrong DATABASE_URL | psql "$DATABASE_URL" -c '\conninfo' |
| routes 500 silently | unhandled async rejection | run with --unhandled-rejections=strict |
| works once then hangs | file-descriptor exhaustion | ulimit -n then raise it |
Common issues & anti-patterns
- Wrong working directory. The app reads config relative to
__dirname/os.getcwd(); launching from the wrong directory silently loads empty or wrong config. Confirm the launch directory. .envnot loaded. dotenv reads from the current directory but the.envsits in a parent, ordotenv.config()runs after the code that readsprocess.env. Load config before first use.- Stale lockfile after a branch switch.
package.jsonchanged but install did not re-run, sonode_modulesholds the wrong version. Re-runnpm ci. - Platform-specific native binary.
bcrypt/sharp/canvascompiled on macOS fail inside a Linux container. Rebuild in the target environment withnpm rebuild. - Silent uncaught async error. On older Node an unhandled rejection does not crash the process; the server "starts" but every route 500s. Add an
unhandledRejectionhandler and inspect logs. - Killing a PID you cannot identify. Never
kill -9a process you have not positively tied to this project — you may take down something unrelated. - Truncating the error. Piping startup to
head -1and missing the real cause three lines down. Capture the full stderr, then read it. - Fixing the symptom, not the cause. Bumping
ulimit -nwhen the real bug is a connection leak that never closes sockets — the limit just delays the crash. rm -rf node_modulesas a reflex. Sometimes right, but it hides the actual mismatch and wastes minutes; checknpm ci --dry-runfirst to see what is really out of sync.- Assuming the dev port. The app prints "listening on 3000" but a config override moved it to 8080; you diagnose the wrong port. Confirm the bound port with
lsof.
Required output
For each diagnostic step report: the command run and its exact output (secrets redacted), the finding (e.g. "port 3000 held by PID 8421 — stale nodemon from a prior session"), the action taken (kill 8421), and the verification (app now responds on localhost:3000). End with a one-sentence root-cause summary.
Safety
- Never print actual secret values from env vars — describe shape only (e.g. "DATABASE_URL set, starts with postgres://").
- Do not run
npm install/pip installwith elevated privileges or outside the project's virtual environment. - Do not kill a process whose PID you cannot positively identify as belonging to this project.
- Make no source or config edits as part of diagnosis; report the cause and the safe fix.
Completion criteria
Done means the startup failure is traced to a named root cause with command-level evidence, the resolving action is applied and verified (the app starts or the dependency responds), and any remaining unresolved factor is flagged with the next safe step — all without exposing secrets or killing unidentified processes.