Skip to content

Troubleshooting ​

SymptomWhat to do
gcloud not installedInstall the Cloud SDK only if you need Google identity. Local-only services still run
ADC unavailable/auth login or gcloud auth application-default login, then devctl auth status / /auth refresh
Wrong projectSet google.project_id or check gcloud config get-value core/project. Identity shows the source
Permission deniedAsk an admin for the missing IAM role; devctl doctor names the resource
Cannot impersonate SANeed roles/iam.serviceAccountTokenCreator on that service account (group binding preferred)
IAP authentication failureConfirm audience, IAP client, and that the identity matches the route. If client_id is set, the ADC refresh token must belong to that OAuth client
Port already in useDoctor lists the holder. Stop a leftover, or change config. Running your own services will also show as “in use”
Service crashesOpen Logs, filter ERROR (e), restart with R
Traces stopped after upgrading to OpenTelemetry JS 3.0, and the process reports no errorSimpleSpanProcessor takes { exporter }. new SimpleSpanProcessor(exporter) exports nothing. The SDK also requires Node 22.15 or later. See Telemetry
Health check failureConfirm the health URL/port; tcp is connect-only; grpc is Health/Check (SERVING); process checks only PID liveness
Proxy unavailable / missing listen portPin proxy.listen.port (required when proxy.enabled is true — validate exits 2). Then devctl proxy start or TUI n. Starting with port 0 exits 7. Bind is loopback only
Token expiredAutomatic refresh uses auth.refresh_threshold_seconds; run devctl auth refresh. Open Doctor if ADC itself expired
Token audience incorrectSet auth.audience on the IAP route; Doctor flags missing audiences
IAP used a user token for an SA routeConfirm the route identity is service_account; Doctor probes impersonated IAP separately
Leftover process after crashReopen devctl — adopt when the pid is alive and the command matches ~/.devctl/state/<hash>/state.json. A start time that only drifted (sleep, WSL clock) still matches; a newer process on that pid does not. A port with no persisted pid is never attached
devctl attach failsNo supervisor. Use devctl start first; attach never starts one
devctl status looks emptyIf the socket is down, status prints persisted state and exits 0 when nothing is running. The TUI still starts a supervisor; leftover PIDs from the previous session appear on the idle dashboard
Start exits 5 or 65 = spawn failed; 6 = health never passed. Doctor then Logs
start brought up extra servicesEmpty start uses the active or first profile alphabetically — YAML key order does not matter (demo: backend, not data). A profile start no longer pulls omitted dependencies. Named devctl start invoices-api (no profile) still expands the local closure
Docker / Podman missing or daemon downA service in config declares container (the demo's postgres always does, even if you never start data). Install and start that runtime, or drop the service. Default profiles do not start postgres
Doctor offers to kill a busy port that is DockerIt should not: ports owned by a running container service are healthy, and Doctor never offers to terminate the Docker or Podman daemon. Re-run Doctor after the container is up
MCP tools work but nothing startsget_status reports setup_mode: true — there is no .devctl yet. Have the agent call get_setup_guide, search_docs, and validate_config, write the files, then reload_config
devctl exec --print-env hides valuesSecret-like names are redacted unless you also pass --reveal (TUI: /exec <svc> --print-env --reveal or /reveal). MCP exec_service never reveals them
TUI env pane looks incompleteIt now loads the same resolved map as devctl exec --print-env. If the chip says config fallback, the daemon exec call failed — check /daemon and that the supervisor is up
TUI says Configuration error but devctl config validate is cleanThe supervisor failed to start (often EADDRINUSE). The TUI now shows Supervisor failed to start with the bootstrap-log line. devctl daemon logs has the same text
Supervisor will not startdevctl daemon logs or TUI /daemon is the bootstrap stderr, not the service log bus. unable to listen … (EADDRINUSE) means a leftover already holds that port — Doctor names the holder
TUI stale / not updatingTUI follows the event bus (20–50ms batch). Quit and let a new supervisor start if an old one is still listening
status or logs_page timed out after 30000ms after sleep, and restart or quit hangsA daemon that is still making progress (heartbeat worker ticking, RPC younger than 60s) is left alone. Quit returns without waiting on the dead connection. A daemon with no heartbeat — one left running by an older devctl — is not replaced automatically; stop it with devctl down --force, then start again. A proven 60s wedge is replaced and services that are still listening are adopted
Dev container fills disk or the daemon freezes under a log floodLogs are queued, then spilled to a 1 GiB spool, then the service is paused instead of killed. Persistence stops if the disk is full; the live window stays. Health lines are recorded on state changes, not every probe. See Logs
Reload needs a restartdevctl reload and /reload list services whose command, env, ports, or identity changed
Configuration invaliddevctl config validate — unknown fields, cycles, and missing refs fail closed. TUI v / /buffer validates before write
Config on disk is broken but the TUI still opens fineExpected: it attached to an already-running daemon and is showing its config_snapshot (last-known-good), not a fresh reparse of the broken file. Fix the file and /reload
devctl update says unavailableGitHub Releases API could not be reached. For npm: npm install --global @amr-m-abdelgawad/devctl@latest. Homebrew and GitHub binaries: see installation
MCP agent cannot connectListener is off by default. /mcp or devctl mcp --on. URL is loopback only; snippets include the bearer token
Web UI shows {"error":"forbidden"}The page Host header was not a loopback name. Use the printed http://127.0.0.1:<port>/ (or localhost / [::1]), not a machine or Dev Container hostname. Releases before 0.8.1 required the Host port to match exactly, so WSL / Dev Container port forwarding and IPv6 localhost could return 403; current releases include the fix. If the issue persists, upgrade to the latest release.
devctl: command not foundRun npm install --global @amr-m-abdelgawad/devctl, then ensure npm's global binary directory is on PATH. See Installation
Bundled Bun runtime was not installedReinstall the npm package without --ignore-scripts; Bun uses its install script to select the correct platform runtime

Internal devctl logs appear in the Logs screen with source devctl.

Released under the MIT License. Use at your own risk.