Troubleshooting
Find your symptom, apply the fix. Start with lazydap doctor --format json — it checks the
things that are usually wrong.
$ lazydap doctor --format json{ "checks": [ { "detail": "none; create /Users/you/.config/lazydap/config.toml to add one", "name": "config.file", "ok": true }, { "detail": "/Users/you/.local/bin/codelldb", "name": "adapter.codelldb", "ok": true }, { "detail": "/opt/homebrew/bin/python3", "name": "adapter.debugpy", "ok": true }, { "detail": "no delve binary found on PATH — install it with `go install github.com/go-delve/delve/cmd/dlv@latest`", "name": "adapter.delve", "ok": false }, { "detail": "/Users/you/lazydap-demo/.lazydap/state.toml (not created yet)", "name": "state.file", "ok": true }, { "detail": "instance lazydap-demo-13cc8efcde46, pid 12102, protocol v10", "name": "daemon", "ok": true } ], "ok": true}An adapter you have not installed is reported and does not fail the run: "ok": true means
lazydap can debug something here. If the daemon itself will not start, doctor says so as a
failed daemon check rather than refusing to run — the checks above it have usually already
named the reason.
A breakpoint never stops anything
Section titled “A breakpoint never stops anything”The program runs to completion and hit_breakpoint_ids is empty.
Check verified first. It is in launch’s reply and in breakpoint_updates on any
--wait reply:
lazydap break --list --format tableverified: false after a launch means the breakpoint is not attached to anything. Three
causes, in order of likelihood:
1. The program is under /tmp on macOS. /tmp is a symlink to /private/tmp, lazydap
sends the resolved path, the compiler recorded the unresolved one, and codelldb compares them
as strings. Move the program under $HOME and rebuild. Full explanation:
quirk 8.
2. The binary has no debug info. Rebuild with -g, and -O0 while you are there — an
optimiser can move or delete the line you asked about.
gcc -g -O0 hello.c -o hello3. The line has no code. A blank line, a comment, or a declaration the compiler emitted nothing for. Pick the next line that does something.
SessionNotPaused
Section titled “SessionNotPaused”{"error":"SessionNotPaused","message":"... is running; pause it first (`lazydap pause --wait`) or wait for a breakpoint"}stack, scopes, variables and eval need a stopped program. Either pause it:
lazydap pause --wait --format jsonor set a breakpoint and continue --wait to it.
This most often follows a --wait that returned "state": "timeout". A timeout does not
pause anything — the program is still running, and that is deliberate.
StaleHandle
Section titled “StaleHandle”{"details":{"handle":2,"kind":"variables reference","stale":true},"error":"StaleHandle","message":"StaleHandle: variables reference 2 belongs to an earlier stop; the program has moved since. Ask `lazydap scopes` or `lazydap variables` again for this one"}A variables_reference and a frame_id are lazydap’s own handles, minted per stop and
never reused. They are valid only for the stop they came from, so any step or continue
invalidates them — and one from a session that has ended says that instead.
The refusal happens before the debugger is asked anything, which is the point: the adapter’s own numbers get recycled, so a stale one could otherwise be answered with another stop’s variables under exit 0.
Call scopes again after every stop and use the new numbers.
SessionAlreadyActive
Section titled “SessionAlreadyActive”One session per daemon. End the current one:
lazydap disconnectOr run the second program under a different daemon:
LAZYDAP_INSTANCE=other lazydap launch ./other-programA session whose program already finished is cleared automatically, so this usually means something is still paused somewhere.
VersionMismatch
Section titled “VersionMismatch”A daemon from an earlier build is still running, typically right after a rebuild.
lazydap shutdownThe next command starts a current one. Nothing is lost except the live session; breakpoints are on disk.
AdapterNotFound
Section titled “AdapterNotFound”codelldb is not on PATH. details.searched lists where lazydap looked.
Adapter discovery is PATH-only today — the config file that would let you point at an
arbitrary binary is specified and not built. Follow the install
guide, and check the wrapper script is executable:
codelldb --helpcodelldb dies immediately, or dlopen fails
Section titled “codelldb dies immediately, or dlopen fails”called Result::unwrap() on an Err value: "dlopen(.../liblldb.dylib, ...) (no such file)"You installed codelldb as a symlink. It resolves liblldb relative to argv[0], so a symlink
sends it one directory too high. Replace it with a wrapper script —
quirk 1 has the
exact three lines.
Every codelldb invocation hangs, including --help
Section titled “Every codelldb invocation hangs, including --help”No output at all, forever, right after a macOS update. macOS has a stale per-inode security record for that file.
Re-copy the install so every file gets a new inode:
rm -rf ~/.local/opt/codelldbcp -R <source>/extension ~/.local/opt/codelldb/extensiontimeout 8 codelldb --helpDetails: quirk 5.
eval says “use of undeclared identifier”
Section titled “eval says “use of undeclared identifier””The variable is not in scope where the program is stopped. This is near-universal right after
--stop-on-entry, which stops before main — the stack says _dyld_start and none of your
program’s variables exist yet.
Set a breakpoint past the declaration and continue --wait before evaluating anything.
If instead you get a memory read error, you are on --context repl, which runs an LLDB
command rather than evaluating an expression. The default is watch and does the right thing
— quirk 7.
--stop-on-entry reports "reason": "exception"
Section titled “--stop-on-entry reports "reason": "exception"”It is not an error. codelldb implements entry-stop with a SIGSTOP, which LLDB files as an
exception. lazydap reports the normalised "reason": "entry" and keeps the adapter’s word in
raw_reason. Branch on reason.
A --wait returned "state": "timeout"
Section titled “A --wait returned "state": "timeout"”Nothing settled inside the window, and the program is still running. Either it is slow or it is stuck, and lazydap does not guess.
lazydap continue --wait --timeout 120 # give it longerlazydap continue --wait --timeout 0 # wait forever; now it is your problemlazydap pause --wait # stop it and look at where it got toLAZYDAP_TIMEOUT changes the 30-second default for every command.
The TUI lost the daemon
Section titled “The TUI lost the daemon”It reconnects on its own, and starts a daemon if there is none — so a lazydap shutdown or a
crash resolves itself. If it stays stuck, quit with q and start it again, then check
lazydap logs --level warn for what the daemon made of it.
Nothing above matches
Section titled “Nothing above matches”Read the daemon’s log. It records what the daemon and the adapter said to each other, which is usually more specific than the error that reached your terminal:
lazydap logs --level warnlazydap logs --limit 50Then open an issue with that output, your
platform, and lazydap version --format json.
See also
Section titled “See also”- codelldb quirks — the same failures, with causes
- Errors and exit codes — organised by error name
- The daemon — logs, paths, instances