Troubleshooting
Symptom-first fixes for heretic-cli — startup and Docker problems, configuration and secret failures, MCP, sidecars, SSH, root mode, session state, and how to gather a useful diagnostic bundle
First moves
heretic-cli doctor # environment health
heretic-cli agents validate <name> # profile shape
heretic-cli local-validate <name> # resolved config (runs secret scripts)
heretic-cli -V run <name> # verbose start-up
heretic-cli --log-file ./heretic.log -V run <name>
Verbose goes before the subcommand
heretic-cli -V run claude enables verbose logging. heretic-cli run claude --verbose passes --verbose into the container instead, because everything after the agent name is the container command.
Configuration errors
| Message | Fix |
|---|---|
Profile '<x>' not found. | heretic-cli agents list; profile files live in ~/.heretic/agents/ |
Invalid profile '<x>': … | heretic-cli agents validate <x>, then fix the listed fields |
Failed to load local config from …: Invalid config: expected an object | the local override is all comments (fresh local-init) — uncomment a key, add extends: <profile>, or delete the file |
Local config extends 'a' but loading profile 'b' | the override belongs to another profile; rename the file or fix extends |
Config validation failed: … | see Validation for each message |
Volume[0].source must be an absolute path: ~/code | use ${HOME}/code or ${CWD} — ~ is not expanded in volume sources |
Environment variable 'PORT' must be a string (got number) | quote it: PORT: "8080" |
ssh.key_path must be an absolute path: ~/.ssh/id_rsa | use ${HOME}/.ssh/id_rsa |
Docker problems
| Symptom | Cause and fix |
|---|---|
Docker is not available. Start Docker and try again. | the daemon is not running, or your user cannot reach the socket |
Failed to get Docker version: Was there a typo in the url or port? | the Docker client library could not reach the socket at all — check DOCKER_HOST, Docker Desktop, or group membership |
docker compose v2 is not available | the compose runner needs the v2 plugin (docker compose version) |
attach fails but the container is running | attach shells out to docker attach — the docker CLI must be on PATH, not just the socket |
Failed to pull image <image>: … | wrong tag, private registry (docker login), or no network — heretic-cli doctor includes a registry reachability check |
| API version too old | Docker API ≥ 1.41 is required; upgrade Docker |
Secrets
| Symptom | Cause and fix |
|---|---|
Secret script not found: <path> | wrong path in secrets:; ~ is expanded, relative paths are not |
Secret script failed (<path>): <stderr> | the script exited non-zero — run it directly in your shell |
Secret "X": environment variable "Y" is not set | a $VAR reference with nothing behind it; export it or switch to a script |
| the variable arrives empty in the container | the script printed nothing (a warning is logged) — empty values are stripped so they cannot shadow image defaults |
| Windows: the script never runs | the extension decides the shell — .cmd/.bat → cmd.exe, .ps1 → PowerShell, .sh → bash via Git Bash or WSL |
Inspect what would actually be used:
heretic-cli agents show <name> --resolved # masked
heretic-cli agents show <name> --resolved --reveal # plaintext, be careful
Agent behaviour
Claude Code shows the login screen. The onboarding seed lives in the session directory. Reset it:
rm -rf .heretic/temp/default/
heretic-cli run claude
Copilot tokens missing inside the container. Copilot tokens are only injected for provider: copilot. Set the field explicitly — when it is absent the provider is inferred from agent_type (copilot-cli → copilot, everything else → anthropic).
A model or auth variable behaves unexpectedly. OAuth mode deliberately sets ANTHROPIC_AUTH_TOKEN: "" and ANTHROPIC_BASE_URL: ""; those empty values are preserved on purpose, while any other empty value is removed. ANTHROPIC_API_KEY is rewritten into ANTHROPIC_AUTH_TOKEN and ANTHROPIC_AUTH_KEY. See Environment Variables.
Settings changes have no effect. The container reads the merged settings written into the session directory. Check the merge inputs: the profile's claude_settings file and .heretic/cli/claude-settings.json.
MCP
| Symptom | Cause and fix |
|---|---|
| the agent has no MCP servers | your project already has a usable .mcp.json, so heretic did not mount its own — set mcp_override: true or edit that file |
| servers vanished after adding one locally | mcp is replaced by the override layer — re-list every server |
Invalid --mcp value: MCP config must be a JSON array of server objects | --mcp takes an array; the mcpServers map form belongs in agents mcp / mcp_file |
MCP file not found: <path> | mcp_file path is wrong after interpolation — check ${CWD} |
| Copilot ignores the config | it must be at ~/.copilot/mcp-config.json; that only happens with agent_type: copilot-cli |
docker exec <container> cat /workspace/.mcp.json
cat .heretic/temp/default/.mcp.json
Build sidecars
# 1. is the wiring there?
docker exec <container> printenv BUILD_SIDECARS
# 2. which commands were wrapped?
docker exec <container> ls /opt/sidecar/wrappers/
# 3. is the builder healthy and reachable?
docker ps --filter label=heretic.role=build-sidecar
docker exec <container> curl -s http://builder-node:8080/health
# 4. test the client directly
docker exec <container> /opt/sidecar/sidecar-exec node npm --version
# 5. builder logs
docker logs heretic-builder-node-default-1a2b3c4d
| Symptom | Cause and fix |
|---|---|
npm: command not found, no wrappers at all | the image has no baked ENTRYPOINT — rebuild with heretic-cli image build, or run with --root, which bind-mounts the current entrypoint |
FATAL: /opt/sidecar/wrappers is not writable by agent (uid 1000) | the image was built with a different --agent-uid, or extra.user changed it |
sidecar-exec: BUILD_SIDECARS is not set | no sidecar configured for this run |
no sidecar available for runtime 'x' | that runtime is not in BUILD_SIDECARS — typo, or --disable-sidecars |
build sidecar '…' did not become healthy within 60s | check the builder logs, raise ready_timeout, confirm the image runs exec-server |
tool_backends: no volume targets '/workspace' | add a volume whose target equals workspace_target |
| the command runs locally instead of in the builder | the tool exists in the agent image, so it was never wrapped — native always wins |
| build output owned by root | run_as_caller_uid was disabled, or a custom builder image forces root |
| builders unreachable | extra.network pinned the agent elsewhere (a warning is logged) |
SSH backend
docker exec <container> env | grep SSH_
docker exec <container> ls -la /home/agent/.ssh/id_rsa
docker exec <container> /opt/sidecar/ssh-exec npm --version
docker exec <container> ssh -vvv -o StrictHostKeyChecking=no \
-p "${SSH_PORT:-22}" "${SSH_USER:-agent}@${SSH_HOST}" 'echo connected'
| Symptom | Cause and fix |
|---|---|
ssh-exec: SSH_HOST is not set | no ssh: block in the resolved config |
Permission denied (publickey) | the public key is not in the remote authorized_keys, or the key has a passphrase (which nothing can answer) |
host.docker.internal does not resolve | Linux — use the bridge gateway address (often 172.17.0.1) or add a host-gateway entry |
| builds compile stale code | the remote host_cwd tree is not your workspace; nothing is synced for you |
Root mode
| Symptom | Explanation |
|---|---|
docker exec shows HOME=/root | expected — exec is a fresh login that does not inherit the entrypoint's exports; the agent session (PID 1) has HOME=/home/agent |
configuration ignored under --root on an old image | the image predates the baked entrypoint; --root bind-mounts the current entrypoint, so rebuild if other features misbehave too |
| files created as root on the host | run without --root, or rebuild the image with --agent-uid $(id -u) |
Containers and sessions
| Task | Command |
|---|---|
| see agents (sidecars hidden) | heretic-cli ps |
| see everything heretic created | docker ps -a --filter label=heretic.managed=true |
| logs | docker logs heretic-<agent>-<session>-<hash8> |
| a second shell | docker exec -it <container> bash |
| stop everything and drop per-run networks | heretic-cli stop --all -f |
| reset an agent's state | rm -rf .heretic/temp/<session> |
heretic-cli stop with no argument matches heretic.project against the current directory exactly — from a subdirectory or a symlinked path it finds nothing, so pass the agent or container name instead.
Self-update
| Symptom | Cause and fix |
|---|---|
update says the CLI was installed via npm/bun | run bun update -g @giglabo/heretic-cli |
Cannot update: insufficient permissions | the binary lives in a system directory — use sudo, or reinstall under ~/.local/bin |
the first command after update does nothing | it applied the staged update and exited; run the command again |
| a locally built binary was replaced | update always installs the latest release — keep dev builds off your PATH |
Known issues in 0.1.0
doctordoes not validate global profiles. Checks 6, 8 and 10 report0 profile(s) validated, always find all images "available", and never check global volume paths. Useagents validateandlocal-validateinstead.- A fresh
local-inittemplate breaksrun. The generated file is all comments and parses tonull; uncomment a key or delete the file. psUPTIME is container age, not time since the last start.- The compose runner's
isRunning()expects a JSON array fromdocker compose ps --format json; newer Compose versions emit newline-delimited JSON and the check reports "not running".heretic-cli ps(label-based) is unaffected.
Diagnostic bundle for a bug report
heretic-cli -v
heretic-cli --log-file ./doctor.log doctor
heretic-cli --log-file ./run.log -V run <agent> -d
heretic-cli ps --json > ps.json
heretic-cli agents show <agent> --resolved > resolved.yaml # masked, safe to share
docker version > docker.txt
Redact before sharing
local-validate output and agents show --reveal contain plaintext secrets. agents show --resolved without --reveal masks sensitive values and is the safe one to attach.
Next Steps
- doctor — what each check means
- Validation — every error message
- Environment Variables — what the container receives