JustHandled Labs
// local environment diagnosis

App won't run locally? Check the environment before the code.

A missing runtime, stale dependency tree, absent environment variable, occupied port, or stopped service can make correct application code look broken. Diagnose those boundaries in a fixed order and keep the evidence.

9 minute guide · updated July 22, 2026
Quick answer

Reproduce the exact failure once. Then check, in order: project root and start command, runtime version, dependency state, required environment-variable names, the requested port, and required background services. Do not expose secret values, kill an unidentified process, or rewrite application code before the evidence points there.

Use one diagnostic order

Randomly reinstalling packages, changing ports, and editing configuration destroys the state that could explain the failure. Keep each check narrow and record the result before taking the next action.

OrderQuestionEvidence to keepDo not assume
1Am I in the intended project root, using the documented start command?Working directory, detected project files, exact command, first useful errorThe first package.json or app.py found is the right entry point
2Does the installed runtime match the project?Runtime and package-manager versions; version constraints in project filesThe newest runtime is automatically compatible
3Are dependencies present and aligned with the repository?Lockfile type, environment location, missing-module errorA forced reinstall is harmless
4Are required variable names present?Missing key names only, never secret valuesA present but empty value is usable
5Is the requested port already owned?Local port, state, owning PID, process nameThe process should be killed
6Are the database, cache, or containers actually ready?Resolved config, service status, health, focused logsA running container is a healthy dependency

1. Reproduce the failure without changing anything

Start from the repository's documented command. Record the current directory, the command, the exit code, and the first causal error rather than the last line of a long stack trace. Confirm the project type from its own files: package.json, pyproject.toml, requirements.txt, go.mod, Dockerfile, or compose.yaml.

Stop if the command is destructive.

A diagnostic should not delete lockfiles, databases, volumes, build caches, or user data. Preview any cleanup command and identify its target first.

2. Match the runtime and dependency method to the repository

Check the installed runtime before installing anything. For Node, compare node --version with the repository's engines, version file, or documentation. For Python, compare python --version with requires-python and check whether the active interpreter belongs to the project's virtual environment.

Let the repository choose the package manager. npm documents that an existing lockfile drives dependency resolution; Python documents virtual environments as isolated, disposable environments that should be recreated rather than copied between machines. A missing dependency tree is evidence for installation. A dependency tree that exists but was created under another runtime is evidence for a controlled recreation.

Runtime: Node 24.x required / Node 22.x observed
Package manager: npm, package-lock.json present
Dependency state: node_modules missing
Supported next action: install with the repository's documented npm command
Unsupported shortcut: delete every lockfile and try another package manager

3. Compare environment-variable names without exposing values

Compare the names in .env.example or documented configuration against the names present in the local environment. Do not paste the file, print its values, or copy it into an issue. Node's current documentation defines .env files as key-value pairs consumed as environment variables; Docker Compose has its own interpolation and precedence behavior.

4. Identify the owner of the requested port

A port conflict is a fact, not permission to terminate a process. On macOS or Linux, inspect a common development port with lsof -i :3000. On Windows PowerShell, Microsoft documents filtering connections by local port:

Get-NetTCPConnection -LocalPort 3000 |
  Select-Object LocalAddress, LocalPort, State, OwningProcess

Get-Process -Id <OwningProcess>

Once the process is identified, decide whether it is the intended application, an abandoned development server, or another service. Prefer stopping the correct development process cleanly. Change the app's port only when that is the intended configuration, not as a way to hide an unknown listener.

5. Separate service readiness from application readiness

If the app depends on Postgres, Redis, MySQL, or Docker Compose services, inspect their state before changing code. Docker's current guide recommends docker compose config for resolved configuration, docker compose up -d to start services, and focused logs or in-container checks for live debugging. A container marked running can still be unhealthy or unable to reach another service.

6. Know when the environment is no longer the leading hypothesis

If the documented runtime, dependencies, required key names, port, and services all check out and the same application path still fails, preserve the evidence and move inward. Inspect configuration loading, the first failing call, external integration responses, error handling, and any mock or stub path. A healthy environment narrows the fault domain; it does not prove that the build is complete.

Keep a minimal evidence record

Project root:
Start command:
Observed failure and exit code:
Runtime and package-manager versions:
Dependency and lockfile state:
Missing environment-variable names (values omitted):
Requested port and owning process:
Required services and health:
Change made:
Result after one controlled retry:
Next fault domain if unchanged:

This record prevents the next person or agent from repeating random fixes and makes the environment-to-code handoff explicit.

Questions

Should I change the application code when a project will not start locally?

Not first. Reproduce the failure, record the intended start command, then check the project root, runtime version, dependency state, required environment-variable names, listening ports, and required services. Change application code only after the environment is supported by evidence.

How do I check whether a port is already in use?

On macOS or Linux, inspect the port with lsof -i :PORT. On Windows PowerShell, use Get-NetTCPConnection -LocalPort PORT. Record the owning process before deciding whether to stop it or change the app's configured port.

Should I print my .env file while debugging?

No. Compare required key names against the keys that are present without copying secret values into terminals, chat logs, tickets, or screenshots. Treat empty required values as missing, but keep the values private.

When should I recreate dependencies or a Python virtual environment?

Recreate them when the runtime and package-manager evidence points to a stale, missing, or incompatible environment. Follow the repository's lockfile and documented package manager; do not use a forced reinstall as the first diagnostic step.

What if the environment checks pass but the app still fails?

Preserve the exact error and move to application-level diagnosis. Check the failing path, configuration loading, external integrations, error handling, and any stubs or fake-success behavior. A healthy environment narrows the problem; it does not prove the implementation is correct.

Primary sources

Diagnose the environment before rewriting the app.

Env Doctor is the free first pass. If the environment is healthy and the build still only looks finished, continue to AI Build Sanity Check.