lsp

JR 2025-12-16T01:10:13.348389Z

I've been noticing that clojure-lsp is running out of channel put space when I run tests in a REPL. (error and stack trace in the thread) This happens when I connect to clojure-lsp's REPL from within Calva via nrepl and execute a bunch of tests. It seems to only happen when I run multiple deftests in the REPL via the command line (eg: (cond->if-test)). I haven't noticed it when I run a test case individually (via execute (via evaluate current form in Calva, where I'm evaluating a "testing" form). Is this something I'm doing wrong?

JR 2025-12-16T19:03:22.739669Z

I found a reproducer (for me!) and entered https://github.com/clojure-lsp/clojure-lsp/issues/2172 It seems to be related to repeatedly calling h/load-code . I also noticed that, at least for Fedora 42, clojure-lsp --verbose hangs on main.clj:197:

(defn ^:private handle-action!
  [action options]
  (if (= "listen" action)
    (let [finished @(server/run-lsp-io-server! (:trace-level options) (:log-path options))]
      {:result-code (if (= :done finished) 0 1)})
    (try 
...
The stack indicates that it's stuck waiting:
"main" #3 [453185] prio=5 os_prio=0 cpu=2743.99ms elapsed=10.01s tid=0x00007f395002b9b0 nid=453185 waiting on condition  [0x00007f39547fb000]
   java.lang.Thread.State: WAITING (parking)
	at jdk.internal.misc.Unsafe.park(java.base@25/Native Method)
	- parking to wait for  <0x0000000746a1a1f8> (a java.util.concurrent.CountDownLatch$Sync)
	at java.util.concurrent.locks.LockSupport.park(java.base@25/LockSupport.java:223)
	at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquire(java.base@25/AbstractQueuedSynchronizer.java:790)
	at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireSharedInterruptibly(java.base@25/AbstractQueuedSynchronizer.java:1139)
	at java.util.concurrent.CountDownLatch.await(java.base@25/CountDownLatch.java:230)
	at clojure.core$promise$reify__8621.deref(core.clj:7257)
	at clojure.core$deref.invokeStatic(core.clj:2337)
	at clojure.core$deref.invoke(core.clj:2323)
	at clojure_lsp.main$handle_action_BANG_.invokeStatic(main.clj:197)
	at clojure_lsp.main$handle_action_BANG_.invoke(main.clj:194)
	at clojure_lsp.main$run_BANG_.invokeStatic(main.clj:224)
	at clojure_lsp.main$run_BANG_.doInvoke(main.clj:217)
	at clojure.lang.RestFn.applyTo(RestFn.java:140)
	at clojure.core$apply.invokeStatic(core.clj:667)
	at clojure.core$apply.invoke(core.clj:662)
	at clojure_lsp.main$main.invokeStatic(main.clj:227)
	at clojure_lsp.main$main.doInvoke(main.clj:226)
	at clojure.lang.RestFn.applyTo(RestFn.java:140)
	at clojure.core$apply.invokeStatic(core.clj:667)
	at clojure.core$apply.invoke(core.clj:662)
	at clojure_lsp.main$_main.invokeStatic(main.clj:252)
	at clojure_lsp.main$_main.doInvoke(main.clj:250)
	at clojure.lang.RestFn.applyTo(RestFn.java:140)
	at clojure_lsp.main.main(Unknown Source)

ericdallo 2025-12-16T19:13:59.133379Z

thanks will take a look later on the issue about that one, I never saw that behavior

👍 1
JR 2025-12-16T01:11:00.695979Z

Here's the repl with the stack trace:

clj꞉clojure-lsp.refactor.transform-test꞉> 
(cond->if-test); 
; ERROR in () (channels.clj:157)
; two cond expressions that are simple
; expected: (= [(h/code "(if is-true" "    :val-is-true" "    :val-is-false)")] (as-strings (do-cond->if (str "(let [is-true true]\n" "  |(cond\n" "     is-true\n" "     :val-is-true\n\n" "     :else\n" "     :val-is-false))"))))
;   actual: java.lang.AssertionError: Assert failed: No more than 1024 pending puts are allowed on a single channel. Consider using a windowed buffer.
; (< (.size puts) impl/MAX-QUEUE-SIZE)
;  at clojure.core.async.impl.channels.ManyToManyChannel.put_BANG_ (channels.clj:157)
;     clojure.core.async$put_BANG_.invokeStatic (async.clj:201)
;     clojure.core.async$put_BANG_.invoke (async.clj:189)
;     clojure_lsp.feature.diagnostics$publish_diagnostic_BANG__STAR_.invokeStatic (diagnostics.clj:132)
;     clojure_lsp.feature.diagnostics$publish_diagnostic_BANG__STAR_.invoke (diagnostics.clj:131)
;     clojure_lsp.feature.diagnostics$publish_diagnostics_BANG_.invokeStatic (diagnostics.clj:146)
;     clojure_lsp.feature.diagnostics$publish_diagnostics_BANG_.invoke (diagnostics.clj:145)
;     clojure_lsp.feature.file_management$did_open.invokeStatic (file_management.clj:52)
;     clojure_lsp.feature.file_management$did_open.invoke (file_management.clj:40)
;     clojure_lsp.handlers$did_open.invokeStatic (handlers.clj:229)
;     clojure_lsp.handlers$did_open.invoke (handlers.clj:224)
;     clojure_lsp.test_helper.internal$load_code.invokeStatic (internal.clj:207)
;     clojure_lsp.test_helper.internal$load_code.invoke (internal.clj:203)
;     clojure_lsp.test_helper.internal$load_code_and_locs.invokeStatic (internal.clj:224)
;     clojure_lsp.test_helper.internal$load_code_and_locs.invoke (internal.clj:219)
;     clojure_lsp.test_helper.internal$load_code_and_locs.invokeStatic (internal.clj:221)
;     clojure_lsp.test_helper.internal$load_code_and_locs.invoke (internal.clj:219)
;     clojure_lsp.test_helper.internal$load_code_into_zloc_and_position.invokeStatic (internal.clj:240)
;     clojure_lsp.test_helper.internal$load_code_into_zloc_and_position.invoke (internal.clj:234)
;     clojure_lsp.test_helper.internal$load_code_and_zloc.invokeStatic (internal.clj:252)
;     clojure_lsp.test_helper.internal$load_code_and_zloc.invoke (internal.clj:247)
;     clojure_lsp.refactor.transform_test$do_cond__GT_if.invokeStatic (transform_test.clj:1670)
;     clojure_lsp.refactor.transform_test$do_cond__GT_if.invoke (transform_test.clj:1668)
;     clojure_lsp.refactor.transform_test$eval2663074.invokeStatic (NO_SOURCE_FILE:1691)
;     clojure_lsp.refactor.transform_test$eval2663074.invoke (NO_SOURCE_FILE:1685)
;     clojure.lang.Compiler.eval (Compiler.java:7700)
;     nrepl.middleware.interruptible_eval$evaluator$run__1447$fn__1458.invoke (interruptible_eval.clj:106)
;     nrepl.middleware.interruptible_eval$evaluator$run__1447.invoke (interruptible_eval.clj:101)
;     nrepl.middleware.session$session_exec$session_loop__1526.invoke (session.clj:229)
;     nrepl.SessionThread.run (SessionThread.java:21)
nil
The config info from Calva is:
Calva is utilizing cider-nrepl and clojure-lsp to create this VS Code experience.
  Effective nREPL dependency versions:
    nrepl: 1.5.1 (Calva defaults)
    cider-nrepl: 0.58.0 (Calva defaults)
    cider/piggieback: 0.6.1 (Calva defaults)

  Latest available nREPL dependency versions found on Clojars:
    nrepl: 1.5.1
    cider-nrepl: 0.58.0
    cider/piggieback: 0.6.1

  clojure-lsp path configured: ./clojure-lsp
And this is on Linux, if that matters. My clojure-lsp is based on a05196a5662e0794aff53981986b57a06b3dd5c5 (I think the latest master), but I did built it myself with extra refactorings that I'm adding.

JR 2025-12-16T01:11:52.829829Z

Once I get this error, I need to restart clojure-lsp. No big deal, but I'm curious if anyone has hints about the why.

ericdallo 2025-12-16T01:17:07.281829Z

yeah the JVM version sometimes gives that, it's something certainly to be improved in clojure-lsp async channels but TBH I don't know the best way to fix it

JR 2025-12-16T01:17:33.896719Z

OK, I mostly wanted to be sure it wasn't something I did!

ericdallo 2025-12-16T01:18:32.598109Z

yeah it's not. it's related to the diagnostics chan

JR 2025-12-16T01:19:02.316649Z

I would enter a bug, but I don't feel confident enough to explain it. 🙂

ericdallo 2025-12-16T01:19:07.468659Z

and it doesn't happen only in tests, but in JVM version of clojure-lsp some times when project has lots of diagnostics to publish

ericdallo 2025-12-16T01:19:19.235989Z

would be nice to find a consistent repro

ericdallo 2025-12-16T01:19:22.223169Z

a start

JR 2025-12-16T01:19:33.389119Z

Ah. I can probably help with that

JR 2025-12-16T01:19:45.375079Z

I'll see if I can make it reproducible

ericdallo 2025-12-16T01:20:39.706449Z

thank you!

2025-12-16T01:59:09.655019Z

The issue is using put! without a callback and using onto-chan! without blocking/parking on the channel it returns. This breaks backpressure allowing publishers to outpace consumers. That may or may not be what the discussion on https://github.com/clojure-lsp/clojure-lsp/pull/1196 is trying to get at, but the code merged for it definitely doesn't fix it.

2025-12-16T02:17:33.531809Z

The fix is either to use >!! to actually block (blocking is back pressure) or may require a larger rearchitecting depending on the context in which those functions are called, which I haven't looked at

ericdallo 2025-12-16T02:59:16.473209Z

yeah, that makes sense and looks like a good try, thanks @hiredman

ericdallo 2025-12-16T02:59:34.110209Z

@john.t.richardson.dev feel free to create a issue if you manage to create a repro so we can try that suggestion