Hi folks, having tried portal in some personal projects I would really like to introduce it into our work env but I am having some trouble.
Seeing as our dev environment runs in a docker container and starts up it’s own nrepl process. I am also starting the portal process with
(p/start {*:port* 8999 *:host* "host.docker.internal"})
(Have also tried starting on 0.0.0.0 to similar results)
Docker then has that port forwarded to my local machine.
At this point all is well and I can even navigate to the portal UI in my browser.
But attempting to call
(p/open {*:launcher* *:vs-code*})
Via the repl always fails with the following:
; Execution error (ExceptionInfo) at portal.runtime.jvm.launcher/remote-open (launcher.clj:44).
; Unable to open extension
Hopefully I am just missing something simple, but I can’t seem to come right 😓Here is the full stacktrace:
; #error {
; :cause "Unable to open extension"
; :data {:options {:launcher :vs-code}, :config {:host "host.docker.internal", :port 8999}, :response {:body "", :headers {:content-length "0", :date "Mon, 11 Nov 2024 12:55:27 GMT", :server "http-kit"}, :status 404}}
; :via
; [{:type clojure.lang.Compiler$CompilerException
; :message "Syntax error macroexpanding at (NO_SOURCE_FILE:37:10)."
; :data #:clojure.error{:phase :execution, :line 37, :column 10, :source "NO_SOURCE_FILE"}
; :at [clojure.lang.Compiler$InvokeExpr eval "Compiler.java" 3719]}
; {:type clojure.lang.ExceptionInfo
; :message "Unable to open extension"
; :data {:options {:launcher :vs-code}, :config {:host "host.docker.internal", :port 8999}, :response {:body "", :headers {:content-length "0", :date "Mon, 11 Nov 2024 12:55:27 GMT", :server "http-kit"}, :status 404}}
; :at [portal.runtime.jvm.launcher$remote_open invokeStatic "launcher.clj" 44]}]
; :trace
; [[portal.runtime.jvm.launcher$remote_open invokeStatic "launcher.clj" 44]
; [portal.runtime.jvm.launcher$remote_open invoke "launcher.clj" 34]
; [portal.runtime.jvm.launcher$eval35764$fn__35765 invoke "launcher.clj" 61]
; [clojure.lang.MultiFn invoke "MultiFn.java" 229]
; [portal.runtime.browser$open invokeStatic "browser.cljc" 170]
; [portal.runtime.browser$open invoke "browser.cljc" 163]
; [portal.runtime.jvm.launcher$open invokeStatic "launcher.clj" 104]
; [portal.runtime.jvm.launcher$open invoke "launcher.clj" 99]
; [portal.api$open invokeStatic "api.cljc" 98]
; [portal.api$open invoke "api.cljc" 78]
; [portal.api$open invokeStatic "api.cljc" 96]
; [portal.api$open invoke "api.cljc" 78]
; [clojure.lang.AFn applyToHelper "AFn.java" 154]
; [clojure.lang.AFn applyTo "AFn.java" 144]
; [clojure.lang.Compiler$InvokeExpr eval "Compiler.java" 3714]
; [clojure.lang.Compiler$DefExpr eval "Compiler.java" 457]
; [clojure.lang.Compiler eval "Compiler.java" 7199]
; [clojure.lang.Compiler eval "Compiler.java" 7149]
; [clojure.core$eval invokeStatic "core.clj" 3215]
; [clojure.core$eval invoke "core.clj" 3211]
; [nrepl.middleware.interruptible_eval$evaluate$fn__33129$fn__33130 invoke "interruptible_eval.clj" 87]
; [clojure.lang.AFn applyToHelper "AFn.java" 152]
; [clojure.lang.AFn applyTo "AFn.java" 144]
; [clojure.core$apply invokeStatic "core.clj" 667]
; [clojure.core$with_bindings_STAR_ invokeStatic "core.clj" 1990]
; [clojure.core$with_bindings_STAR_ doInvoke "core.clj" 1990]
; [clojure.lang.RestFn invoke "RestFn.java" 425]
; [nrepl.middleware.interruptible_eval$evaluate$fn__33129 invoke "interruptible_eval.clj" 87]
; [clojure.main$repl$read_eval_print__9206$fn__9209 invoke "main.clj" 437]
; [clojure.main$repl$read_eval_print__9206 invoke "main.clj" 437]
; [clojure.main$repl$fn__9215 invoke "main.clj" 458]
; [clojure.main$repl invokeStatic "main.clj" 458]
; [clojure.main$repl doInvoke "main.clj" 368]
; [clojure.lang.RestFn invoke "RestFn.java" 1523]
; [nrepl.middleware.interruptible_eval$evaluate invokeStatic "interruptible_eval.clj" 84]
; [nrepl.middleware.interruptible_eval$evaluate invoke "interruptible_eval.clj" 56]
; [nrepl.middleware.interruptible_eval$interruptible_eval$fn__33162$fn__33166 invoke "interruptible_eval.clj" 152]
; [clojure.lang.AFn run "AFn.java" 22]
; [nrepl.middleware.session$session_exec$main_loop__33232$fn__33236 invoke "session.clj" 218]
; [nrepl.middleware.session$session_exec$main_loop__33232 invoke "session.clj" 217]
; [clojure.lang.AFn run "AFn.java" 22]
; [java.lang.Thread run "Thread.java" 840]]}Which suggests that we are 404ing Looking at the source for this exception we are trying to hit the /open route Which also 404s if I try to access it from the browser.
When you open VS code with the portal extension installed, it starts an http server and puts the connection info in .portal/vs-code.edn . Then, when you use `:launcher` `:vs-code` , the REPL process uses that file to communicate to the vs code extension. The problem here is that REPL process in the docker container doesn't have access to your host machines VS Code extension port.
And if it does have access to your host system's ports, it might be that you need to update the :host in the .portal/vs-code.edn file.
Maybe something like docker.for.mac.host.internal is what's needed 🤔
Ahh I see, is there a way to set which port that vs-code web-server opens on initially? Or would I need to open the port after I see what it has chosen? Either way thank you for the context! I had no idea that we were starting two web-servers here.
That info is in the file but is dynamically set per initialization of the plugin, which should be stable until you restart your editor.
Yeah, wish things were simpler
Once I come right would it be helpful if I create a writeup of my process of getting it working? More resources can’t hurt for newcomers I guess.
And just to confirm keeping the port set by vs-code but changing the host to “host.docker.internal” in vs-code.edn sorted out my issue. 💃 Thank you very much @djblue
If we could find a good heuristic for determining if your REPL is running a container, we try swapping out the host automatically 🤔 Another option would be to try all the well know docker aliases for localhost if the first attempt fails.
Instead of manually editing the .portal/vs-code.edn config, one can just provide the host in the launcher-config. Just adding this here in case somebody comes across this thread in the future. Snippet here: https://clojurians.slack.com/archives/CBE668G4R/p1752073642292629?thread_ts=1751957426.120099&cid=CBE668G4R