claude code bug report #55
Labels
No labels
bug
documentation
duplicate
enhancement
good first issue
help wanted
invalid
question
wontfix
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
kahliburke/Kaimon.jl#55
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
I don't know if this is helpful, but below is claude code's own report of some buggy behaviour I encountered.
Kaimon MCP — bug report
Date: 2026-06-23
Context: Driving an iterative Julia loop via the kaimon MCP from an automated coding agent — "edit a source file → re-evaluate in the session → inspect the result", with many small edits to a single package source file and a re-evaluation after each batch.
Identifiers below are placeholders:
MyPkg= the package under development (a path-dev-ed Julia package with its ownProject.toml), at<project-root>. Session keys and PIDs are reproduced as observed (they are random/local and not sensitive).Environment
~/.julia/packages/Kaimon/6N7iB/(version hash6N7iB). Stacktraces point into~/.julia/packages/Kaimon/6N7iB/src/gate.jl.dev-ed Julia package (MyPkg) with a few registered deps; nothing exotic.0f5b58b7, agent-spawned for a different project, PID 45258, up ~7.5h. It never misbehaved — only theMyPkgsession(s) did. Useful as a control.TL;DR — five distinct symptoms
pwd()was an unrelated local project directory, not theproject_pathpassed tostart_session.Revise.revise(); newly-added top-level definitions stayedundefined. Only a full process restart picked changes up.manage_repl("restart")timed out and was a no-op: afterwards the process kept its old PID and accumulating uptime, and ran stale code.start_sessionphantom "already running": it returned a session key thatpingandexsimultaneously reported as nonexistent — and therefore declined to create a new one, leaving the project with no usable REPL.Net effect: the persistent-REPL workflow became unusable. Fell back to spawning a one-shot
julia --project=<project-root> -e '…'from a plain shell, which worked 100% reliably thereafter.What worked (to isolate the fault)
start_session— the first call returned a usable key.run_tests— always worked and always reflected current on-disk code. (It spawns a fresh subprocess, sidestepping both the stale-Revise and registry issues.)exwith absolute paths — worked whenever the session was actually live.manage_repl("restart")— worked the first time; hung the second time.julia --project=<project-root> -e '…'from a plain shell — worked every time. This confirms the on-disk edits were valid and complete; the fault is in the persistent session / Revise / registry layer, not the code.Issue 1 — Fresh session's REPL working directory is not the project directory
Repro:
Result (verbatim, path redacted):
Observation:
pwd()in the new session resolved a project-relative path against an unrelated local project directory, not theproject_pathpassed tostart_session. The REPL apparently inherited a cwd from the MCP server process (or a global/previous default) instead ofcd-ing into the activated project.Expected: a session activated for
project_path = Xshould havepwd() == X(or the cwd behaviour should be documented), so project-relative paths work.Workaround: used an absolute project root constant and
joinpatheverywhere.Severity: low (easy workaround) but surprising; a footgun for any relative-path code.
Issue 2 — Revise does not hot-reload edits to package source in the persistent session
Repro: Edited one package source file repeatedly — changed function bodies, and added three brand-new top-level definitions (one function + two
consts; call themnewfn,NEW_A,NEW_B). Re-ran the workflow viaex.Result: the produced output reflected the old code. Confirmed at the artifact level — the generated output still contained the pre-edit form of a changed function, after that code had been replaced on disk.
Direct confirmation (verbatim):
Even after an explicit
Revise.revise(), none of the newly-added top-level symbols existed in the module. The session banner reported "Revise active."Cross-check:
run_tests(fresh subprocess) and the one-shotjulia --projectdid see all the new code — so the edits were on disk and valid; only the long-lived session's module was stale, and Revise's incremental update never fired.Hypotheses for maintainer:
dev-ed into the active environment / file watcher not started).Revise.revise()being a no-op suggests Revise has no tracked files for this package.Pkg.develop-ed in the active env? Is Revise's FS watcher attached to itssrc/? DoRevise.pkgdatas/Revise.watched_filescontain it? Any queued revision errors (Revise.queue_errors)?Severity: high for an interactive dev loop — it silently produces stale results (no error), the worst failure mode.
Issue 3 —
manage_repl("restart")timed out and did not actually restartFirst restart (worked):
After this, fresh code was loaded — a working restart was the only thing that picked up edits.
Second restart (hung):
Then
pingshowed (verbatim, names redacted):Observation: the
MyPkgprocess showed uptime 1h 37m and an unchanged PID (63715) — i.e. it was not actually restarted (a real restart would show ~0m uptime / a new PID). Yet the call returned a timeout, not success or failure. Note also the key visible toping(438477fc) differed from the key I had restarted (c97c8a8e) — see Issue 4.Severity: high — restart was the only reliable way to pick up edits (given Issue 2), and it became unavailable.
Issue 4 — Session registry race / non-deterministic key reassignment
The set of "available sessions" and the project's session key changed between consecutive calls, with no session activity from me in between. Verbatim sequence (names redacted, keys/PIDs as observed):
Two keys (
c97c8a8e,438477fc) appear and disappear inconsistently;438477fcreappeared after being shut down (step 2 → step 4);ping,start_session, and theex/shutdown"Available:" listings disagreed with each other within seconds.Hypothesis: the key↔process registry has a race / stale-entry bug — possibly
restartregisters a new key/process without atomically retiring the old one, and timeouts (Issue 3) leave half-committed registry state. Different endpoints (pingvsstart_sessionvs theex/shutdown"Available:" list) appear to read different or differently-cached views of the registry.Severity: high — makes the session unaddressable; you can't reliably target a key.
Issue 5 —
start_sessionphantom "already running" (won't create a usable session)In steps 3 and 6 above,
start_sessionreturned:…but at those same moments
pingreported noMyPkgsession (steps 5/8) andex(ses=c97c8a8e)failed with"No session matched 'c97c8a8e'"(step 7). So:start_session's "already running" check consulted a stale registry entry and returned a key that is not actually live/connected.Expected:
start_sessionshould verify the candidate is actually alive (ping the gate / check the PID) before reporting "already running"; if dead/unreachable, reap the stale entry and start a fresh process.Severity: high — this is the terminal failure that forced the fallback.
Consolidated hypotheses & suggested diagnostics
In rough priority:
ping/start_session/ex; reap dead/phantom entries; makerestartatomic (retire the old key/PID together with registering the new); on arestarttimeout, return the actual resulting state and the new key rather than a bare timeout. Havestart_sessionhealth-check the "already running" candidate before returning it.src/at session start. Useful introspection to capture:Revise.pkgdatas,Revise.watched_files,length(Revise.revision_queue),Revise.queue_errors. Check whether the package isdev-ed vs loaded from a precompiled image, and whether the FS watcher thread is alive. A no-opRevise.revise()strongly suggests nothing is being tracked.cdthe gate process intoproject_path(or expose/document the cwd) at session creation.Artifacts that would help (could not be retrieved reliably once the session died):
server_logMCP tool).6N7iB; relevant framessrc/gate.jl:1211 (_eval_with_capture),:920 (gate_eval),:1571,:1610.Reproduction recipe (suspected):
start_sessionfor a path-dev-ed Julia package; checkpwd()in the session (Issue 1).src/file (add a new top-level symbol);exRevise.revise(); isdefined(Mod, :newsym)→ expectfalse(Issue 2).manage_repl("restart")repeatedly / under load until one times out;pingand compare PID / uptime / key (Issues 3–4).start_sessionagain and observe the phantom "already running" plus a failingexagainst that key (Issue 5).@jonalm Thanks, I'll take a look. I may have fixed some of these issues in my work towards 2.0. Can you confirm for me whether you were running the TUI for the kaimon server? Some people were using a headless option which was undocumented and not fully baked.
@jonalm manage_repl definitely works for me consistently so I'm a bit confused about it. I don't get timeouts. I generally am starting REPL sessions myself in the project directory. It seems like your reports have been for Julia sessions kaimon is starting via tool calls? I use that method less frequently so I may need to dig into it.
I have made changes to better associate agents with the sessions in upcoming 2.0 so that might help.
I'll take a look at Revise, the trick for that is Revise hooks into the REPL execution to perform the auto-reload functionality. I've tried to recreate what it does but perhaps I'm missing certain cases.
This is my workflow.
I have kaimon 1.3.1 installed in my default project (as an app).
> kaimonfrom any terminalpath/to/whaterver/project> julia --project, and then runjulia > using Kaimon;Gate.serve()spath/to/whaterver/project> claudeand continue from there@jonalm Thanks for the clarification. That is pretty much how I do things as well, though I use the Julia startup.jl so I don't need to run
Gate.servemanually. In this situation there isn't any need for start_session, since it's already started by you. The agent is generally able to find the session using ping. One thing I do have in my CLAUDE.md file (globally) is an instruction to take theusage_quizwhich for me is like a primer on how to use the system. Is that something you've seen used either organically or by prompting?Not really. Maybe my MEMORY files contains misinformation, I'll try to dig for that. Could you share the kaimon related part of your CLAUDE.md
Thanks @jonalm, this was a really helpful report. The "what worked vs what didn't" breakdown made it much easier to track down, and it surfaced a couple of real weak spots.
Most of these turned out to share a single root cause. In your setup you start the gate yourself with
Gate.serve(), but the agent also calledstart_session, which spun up a second gate for the same project. With two gates for one project, the registry confusion (#4/#5), the restart hitting the wrong process (#3), and the rest followed. That's a tool-design problem, not something you did wrong.Where each piece lands in 2.0:
roots), so the agent finds your running session viapingand doesn't needstart_sessionat all.start_sessionconverges on a gate already connected for the project instead of spawning a duplicate. This is the main one.cdinto the project directory (#1), so project-relative paths resolve correctly instead of against the TUI's working dir.projects.jsonby hand.I haven't reproduced every symptom, so I won't claim it's all airtight, but the root cause is handled. Once 2.0 lands I'd appreciate a fresh run on your workflow, and a new issue if anything still misbehaves. I'll credit you in the 2.0 release notes.
Thanks again.
@jonalm Closing this out as 2.0 has shipped!