---
title: Troubleshooting
description: "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"
canonical: https://giglabo.com/heretic/docs/heretic-cli/reference/troubleshooting
locale: en
---

# Troubleshooting

> Markdown twin of https://giglabo.com/heretic/docs/heretic-cli/reference/troubleshooting
> Fetch this instead of the HTML page: same content, a fraction of the bytes.
> Site structure and the full page list for agents: https://giglabo.com/llms.txt

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

```bash
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>
```

> **Warning: 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](https://giglabo.com/heretic/docs/heretic-cli/reference/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:

```bash
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:

```bash
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](https://giglabo.com/heretic/docs/heretic-cli/reference/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` |

```bash
docker exec <container> cat /workspace/.mcp.json
cat .heretic/temp/default/.mcp.json
```

## Build sidecars

```bash
# 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

```bash
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

- **`doctor` does not validate global profiles.** Checks 6, 8 and 10 report `0 profile(s) validated`, always find all images "available", and never check global volume paths. Use `agents validate` and `local-validate` instead.
- **A fresh `local-init` template breaks `run`.** The generated file is all comments and parses to `null`; uncomment a key or delete the file.
- **`ps` UPTIME is container age**, not time since the last start.
- **The compose runner's `isRunning()` expects a JSON array** from `docker 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

```bash
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
```

> **Warning: 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](https://giglabo.com/heretic/docs/heretic-cli/commands/doctor) — what each check means
- [Validation](https://giglabo.com/heretic/docs/heretic-cli/reference/validation) — every error message
- [Environment Variables](https://giglabo.com/heretic/docs/heretic-cli/reference/environment-variables) — what the container receives

## Related

- HTML version of this page: https://giglabo.com/heretic/docs/heretic-cli/reference/troubleshooting
- Site map for agents: https://giglabo.com/llms.txt
