Error recovery

Understanding ClojureScript errors

Generating a Hoplon project

Generate a new Hoplon project. Run the following command on a terminal:

boot -d boot/new new -t hoplon -n error-recovery

Running the project in development mode

Change your directory to the project:

cd error-recovery

And run:

boot dev

After the code is compiled open your browser and go to http://localhost:8000. You should see "Hello Hoplon!" in blue.

Screenshot with \
Screenshot with "Hello Hoplon" header

Making mistakes on index.cljs.hl file

Let's make some mistake Open the file src/index.cljs.hl inside the project directory, in your favorite editor. You should see the code below:

(page"index.html")(html(head(link:href"app.css":rel"stylesheet":type"text/css"))(body(h1"Hello, Hoplon!")))

Unbalanced parentheses

Not closing a form

In ClojureScript you will get errros if you don't close your forms. Remove one of the parentheses in the end of the (h1 "Hello, Hoplon!")))

(page"index.html")(html(head(link:href"app.css":rel"stylesheet":type"text/css"))(body(h1"Hello, Hoplon!"))

When you save the file you will get the following message on the terminal running boot dev.

Writing HTML files...
• index.html
Adding :require adzerk.boot-reload.init3509 to index.html.cljs.edn...
Compiling ClojureScript...
• index.html.js
adzerk.boot_cljs.util.proxy$clojure.lang.ExceptionInfo$ff19274a: ERROR: EOF while reading, starting at line 3 and column 1 at file hoplon/app_pages/_index_DOT_html.cljs, line 8, column 1

    data: {:type :reader-exception,
           :line 8,
           :column 1,
           :file "hoplon/app_pages/_index_DOT_html.cljs",
           :from :boot-cljs}
adzerk.boot_cljs.util.proxy$clojure.lang.ExceptionInfo$ff19274a: EOF while reading, starting at line 3 and column 1
    data: {:type :reader-exception,
           :line 8,
           :column 1,
           :file
           "/home/y/.dotfiles/dotfiles/boot/cache/tmp/home/y/repos/hoplon/hoplon-project/eb4/6qp7pj/hoplon/app_pages/_index_DOT_html.cljs"}
                                      clojure.core/ex-info/invokeStatic          core.clj: 4617
                                                   clojure.core/ex-info          core.clj: 4617
            clojure.tools.reader.reader-types/reader-error/invokeStatic  reader_types.clj:  389
                         clojure.tools.reader.reader-types/reader-error  reader_types.clj:  385
                                                                    ...
                       clojure.tools.reader/read-delimited/invokeStatic        reader.clj:  201
                                    clojure.tools.reader/read-delimited        reader.clj:  190
                            clojure.tools.reader/read-list/invokeStatic        reader.clj:  210
                                         clojure.tools.reader/read-list        reader.clj:  206
                                clojure.tools.reader/read*/invokeStatic        reader.clj:  916
                                             clojure.tools.reader/read*        reader.clj:  897
                                 clojure.tools.reader/read/invokeStatic        reader.clj:  965
                                              clojure.tools.reader/read        reader.clj:  942
cljs.analyzer$forms_seq_STAR_$forms_seq___2315$fn__2316$fn__2317.invoke     analyzer.cljc: 3112
         cljs.analyzer$forms_seq_STAR_$forms_seq___2315$fn__2316.invoke     analyzer.cljc: 3105
                                                                    ...
                                          clojure.core/seq/invokeStatic          core.clj:  137
                                                       clojure.core/seq          core.clj:  137
                                 cljs.compiler$emit_source.invokeStatic     compiler.cljc: 1255
                                       cljs.compiler$emit_source.invoke     compiler.cljc: 1237
                       cljs.compiler$compile_file_STAR_$fn__3623.invoke     compiler.cljc: 1328
                              cljs.compiler$with_core_cljs.invokeStatic     compiler.cljc: 1159
                                    cljs.compiler$with_core_cljs.invoke     compiler.cljc: 1150
                          cljs.compiler$compile_file_STAR_.invokeStatic     compiler.cljc: 1317
                                cljs.compiler$compile_file_STAR_.invoke     compiler.cljc: 1313
                             cljs.compiler$compile_file$fn__3646.invoke     compiler.cljc: 1398
                                cljs.compiler$compile_file.invokeStatic     compiler.cljc: 1376
                                      cljs.compiler$compile_file.invoke     compiler.cljc: 1356
                                 cljs.closure/compile-file/invokeStatic       closure.clj:  432
                                              cljs.closure/compile-file       closure.clj:  423
                                               cljs.closure/eval5411/fn       closure.clj:  499
                                             cljs.closure/eval5347/fn/G       closure.clj:  389
                                               cljs.closure/eval5417/fn       closure.clj:  508
                                             cljs.closure/eval5347/fn/G       closure.clj:  389
                                   cljs.closure/compile-sources/iter/fn       closure.clj:  829
                                                                    ...
                                         clojure.core/next/invokeStatic          core.clj:   64
                                        clojure.core/dorun/invokeStatic          core.clj: 3033
                                        clojure.core/doall/invokeStatic          core.clj: 3039
                                                     clojure.core/doall          core.clj: 3039
                              cljs.closure/compile-sources/invokeStatic       closure.clj:  825
                                           cljs.closure/compile-sources       closure.clj:  814
                                        cljs.closure/build/invokeStatic       closure.clj: 1945
                                                     cljs.closure/build       closure.clj: 1883
                                      cljs.build.api/build/invokeStatic           api.clj:  199
                                                   cljs.build.api/build           api.clj:  187
                        adzerk.boot-cljs.impl/compile-cljs/invokeStatic          impl.clj:   89
                                     adzerk.boot-cljs.impl/compile-cljs          impl.clj:   66
                                                                    ...
                                        clojure.core/apply/invokeStatic          core.clj:  646
                                                     clojure.core/apply          core.clj:  641
                                                  boot.pod/eval-fn-call           pod.clj:  328
                                                      boot.pod/call-in*           pod.clj:  379
                                                                    ...
                                                      boot.pod/call-in*           pod.clj:  382
                                  adzerk.boot-cljs/compile/invokeStatic     boot_cljs.clj:   71
                                               adzerk.boot-cljs/compile     boot_cljs.clj:   59
                                          adzerk.boot-cljs/compile-1/fn     boot_cljs.clj:  126
                                    clojure.core/binding-conveyor-fn/fn          core.clj: 1938
                                                                    ...
Elapsed time: 0.211 sec

While this is not a very nice message it sure tells you that you have a problem. But this gets better. If you look at your browser window there is something there for you too:

Screenshot with a cljs error message
Screenshot with a cljs error message of parentheses not closed

The important part of the error displayed on the terminal shows at your browser too.

The error is ERROR: EOF while reading, starting at line 3 and column 1 at file hoplon/app_pages/_index_DOT_html.cljs, line 8, column 1. An EOF usually means that the file ended but the form was incomplete. So you probably have an open parenthesis without a closing one. starting at line 3 and column 1 is the location of the open parenthesis that was not closed.

Wait a minute. We didn't close the parenthesis from the (h1 form. Well, the reader has no way to know that. The parentheses are closed in the order reversed, so the last opened one is closed first. In the end there is one parenthesis that was not closed and that parenthesis was opened on line 3, column 1.

You can now fix the src/index.cljs.hl adding the removed parenthesis back.

Closing a form not opened

Also if you close a form that was not opened you will get errors. Add one extra closing parenthesis in the end of the (ns "index.html")))

(page "index.html"))

(html
  (head
    (link :href "app.css" :rel "stylesheet" :type"text/css"))
  (body
    (h1 "Hello, Hoplon!")))

When you save the file you will get the following message on the terminal running boot dev.

Writing HTML files...
• index.html
Adding :require adzerk.boot-reload.init3509 to index.html.cljs.edn...
Compiling ClojureScript...
• index.html.js
adzerk.boot_cljs.util.proxy$clojure.lang.ExceptionInfo$ff19274a: ERROR: Unmatched delimiter ) at file hoplon/app_pages/_index_DOT_html.cljs, line 1, column 1598

    data: {:type :reader-exception,
           :line 1,
           :column 1598,
           :file "hoplon/app_pages/_index_DOT_html.cljs",
           :from :boot-cljs}
adzerk.boot_cljs.util.proxy$clojure.lang.ExceptionInfo$ff19274a: Unmatched delimiter )
    data: {:type :reader-exception,
           :line 1,
           :column 1598,
           :file
           "/home/y/.dotfiles/dotfiles/boot/cache/tmp/home/y/repos/hoplon/hoplon-project/dvg/6qp7pj/hoplon/app_pages/_index_DOT_html.cljs"}
                                      clojure.core/ex-info/invokeStatic          core.clj: 4617
                                                   clojure.core/ex-info          core.clj: 4617
            clojure.tools.reader.reader-types/reader-error/invokeStatic  reader_types.clj:  389
                         clojure.tools.reader.reader-types/reader-error  reader_types.clj:  385
                                                                    ...
             clojure.tools.reader/read-unmatched-delimiter/invokeStatic        reader.clj:   76
                          clojure.tools.reader/read-unmatched-delimiter        reader.clj:   74
                                clojure.tools.reader/read*/invokeStatic        reader.clj:  916
                                             clojure.tools.reader/read*        reader.clj:  897
                                 clojure.tools.reader/read/invokeStatic        reader.clj:  965
                                              clojure.tools.reader/read        reader.clj:  942
cljs.analyzer$forms_seq_STAR_$forms_seq___2315$fn__2316$fn__2317.invoke     analyzer.cljc: 3112
         cljs.analyzer$forms_seq_STAR_$forms_seq___2315$fn__2316.invoke     analyzer.cljc: 3105
                                                                    ...
                                          clojure.core/seq/invokeStatic          core.clj:  137
                                                       clojure.core/seq          core.clj:  137
                                 cljs.compiler$emit_source.invokeStatic     compiler.cljc: 1255
                                       cljs.compiler$emit_source.invoke     compiler.cljc: 1237
                       cljs.compiler$compile_file_STAR_$fn__3623.invoke     compiler.cljc: 1328
                              cljs.compiler$with_core_cljs.invokeStatic     compiler.cljc: 1159
                                    cljs.compiler$with_core_cljs.invoke     compiler.cljc: 1150
                          cljs.compiler$compile_file_STAR_.invokeStatic     compiler.cljc: 1317
                                cljs.compiler$compile_file_STAR_.invoke     compiler.cljc: 1313
                             cljs.compiler$compile_file$fn__3646.invoke     compiler.cljc: 1398
                                cljs.compiler$compile_file.invokeStatic     compiler.cljc: 1376
                                      cljs.compiler$compile_file.invoke     compiler.cljc: 1356
                                 cljs.closure/compile-file/invokeStatic       closure.clj:  432
                                              cljs.closure/compile-file       closure.clj:  423
                                               cljs.closure/eval5411/fn       closure.clj:  499
                                             cljs.closure/eval5347/fn/G       closure.clj:  389
                                               cljs.closure/eval5417/fn       closure.clj:  508
                                             cljs.closure/eval5347/fn/G       closure.clj:  389
                                   cljs.closure/compile-sources/iter/fn       closure.clj:  829
                                                                    ...
                                         clojure.core/next/invokeStatic          core.clj:   64
                                        clojure.core/dorun/invokeStatic          core.clj: 3033
                                        clojure.core/doall/invokeStatic          core.clj: 3039
                                                     clojure.core/doall          core.clj: 3039
                              cljs.closure/compile-sources/invokeStatic       closure.clj:  825
                                           cljs.closure/compile-sources       closure.clj:  814
                                        cljs.closure/build/invokeStatic       closure.clj: 1945
                                                     cljs.closure/build       closure.clj: 1883
                                      cljs.build.api/build/invokeStatic           api.clj:  199
                                                   cljs.build.api/build           api.clj:  187
                        adzerk.boot-cljs.impl/compile-cljs/invokeStatic          impl.clj:   89
                                     adzerk.boot-cljs.impl/compile-cljs          impl.clj:   66
                                                                    ...
                                        clojure.core/apply/invokeStatic          core.clj:  646
                                                     clojure.core/apply          core.clj:  641
                                                  boot.pod/eval-fn-call           pod.clj:  328
                                                      boot.pod/call-in*           pod.clj:  379
                                                                    ...
                                                      boot.pod/call-in*           pod.clj:  382
                                  adzerk.boot-cljs/compile/invokeStatic     boot_cljs.clj:   71
                                               adzerk.boot-cljs/compile     boot_cljs.clj:   59
                                          adzerk.boot-cljs/compile-1/fn     boot_cljs.clj:  126
                                    clojure.core/binding-conveyor-fn/fn          core.clj: 1938
                                                                    ...
Elapsed time: 0.385 sec

While this is not a very nice message it sure tells you that you have a problem. But this gets better. If you look at your browser window there is something there for you too:

Screenshot with a cljs error message
Screenshot with a cljs error message of closing parentheses not open

The important part of the error displayed on the terminal shows at your browser too.

The error is ERROR: Unmatched delimiter ) at file hoplon/app_pages/_index_DOT_html.cljs, line 1, column 1598. An Unmatched delimiter ) is a straightforward message at least. It means that the there is a closing ) without one being opened.starting at line 1 and column 1598 is the location of the open parenthesis that was not closed. The column number seems like an error but during the expansion of the page macro the ns form that is generated is:

(ns hoplon.app-pages._index_DOT_html (:require[javelin.core :refer[->Cell input? cell cell? destroy-cell! lift lens? set-formula! cell-doseq* deref* set-cell! lens formula? alts! dosync* cell-map formula]][hoplon.core :refer[form audio input menuitem hgroup do! timeout $text base h1 set-attributes! embed h3 body keygen progress main cite on-page-load object i p nav ruby check-val! a menu blockquote img $comment span track data u dl select html thead del eventsource fieldset aside figure figcaption q on! bdi append-child! video address caption dd rp hr tbody table acronym frame applet html-var add-initfn! pre ul dir replace-child! html-time html-map sup dfn sub mark script big button wbr insert-before! strong li dt rtc frameset td tr section th optgroup bust-cache iframe remove-child! legend em kbd spliced article isindex abbr command prerendering? <!-- source output set-styles! basefont route-cell header datalist tfoot s ins footer title h5 canvas param font div option ensure-kids! summary samp center small style textarea loop-tpl* strike h4 tt head ol details col label rt when-dom h6 link page-load colgroup meter html-meta static-elements text-val! bdo --> b code dialog noframes do-watch noscript safe-nth h2 area br]])(:require-macros[javelin.core :refer[with-let mx2 dosync cell= set-cell!= prop-cell cell-doseq defc cell-let-1 defc= macroexpand-all mx cell-let]][hoplon.core :refer[text elem+ cache-key with-timeout defelem+ when-tpl static sexp defelem elem def-values if-tpl cond-tpl with-page-load for-tpl with-dom case-tpl loop-tpl with-interval with-init!]]))

So the extra closing parenthesis is at column 1598.

The error will be the same except by the character itself if you close a ] or a } that is not open. So the message in this case would be ERROR: Unmatched delimiter ] at file hoplon/app_pages/_index_DOT_html.cljs, line 1, column 1598 or ERROR: Unmatched delimiter } at file hoplon/app_pages/_index_DOT_html.cljs, line 1, column 1598.

You can now fix the src/index.cljs.hl removing the extra parenthesis.

Undeclared variables

Another common error is using a variable not declared yet In ClojureScript variables have to be declared before they are used. Or you can mistype a variable.

Let's edit src/index.cljs.hl replacing (h1 with (h7. h7 doesn't exist so it's not declared. What happens?

On the browser you will see:

Screenshot with a cljs warning message
Screenshot with a cljs warning message of undeclared variable

On the terminal you can see the same warning:

Writing HTML files...
• index.html
Adding :require adzerk.boot-reload.init3509 to index.html.cljs.edn...
Compiling ClojureScript...
• index.html.js
WARNING: Use of undeclared Var hoplon.app-pages._index_DOT_html/h7 at line 7 hoplon/app_pages/_index_DOT_html.cljs
Elapsed time: 0.302 sec

Let's explain the error. Use of undeclared Var describes the problem. So a variable that has not being declared was used. Next is the variable itself: hoplon.app-pages._index_DOT_html/h7. Well, where did that come from?

When you see a variable or a keyword in ClojureScript they may be qualified or unqualified. Qualified variables are in the form namespace/variable-name and unqualified ones are simply variable-name. We will discuss namespaces in more depth later but for now you need to know that hoplon.app-pages._index_DOT_html is a namespace and h7 is a variable name.

The namespace hoplon.app-pages._index_DOT_html was generated by Hoplon when we used the page macro in (page "index.html"). So the rule is that a namespace will be generated with the format hoplon.app-pages.page-name_DOT_html. In our case page-name is index.

By default namespaces in .hl files have all variables from ClojureScript core, Hoplon core and Javelin core refered. This means that we can use then unqualified. But when we use a variable not declared on those namespaces the compiler complains saying that it could not find the variable on the namespace that's trying to use it. That's why the message is Use of undeclared Var hoplon.app-pages._index_DOT_html/h7.

Next we have at line 7 hoplon/app_pages/_index_DOT_html.cljs. That says the line where the variable was used and the file name. In ClojureScript there is a correspondence between namespaces names and file paths. The original index.cljs.hl file is transformed by boot-hoplon in the hoplon/app_pages/_index_DOT_html.cljs file with the namespace hoplon.app-pages._index_DOT_html as we saw before.

You can now fix the src/index.cljs.hl replacing h7 with h1.

Next steps

We will understand a bit more about ClojureScript itself.