# Cram test errors and \*\*\*\* UNREACHABLE \*\*\*\*

**URL:** https://discuss.ocaml.org/t/cram-test-errors-and-unreachable/17697
**Category:** Ecosystem
**Tags:** testing, dune, cram
**Created:** [January 15, 2026, 10:08am UTC](https://discuss.ocaml.org/t/cram-test-errors-and-unreachable/17697 "2026-01-15T10:08:35Z")
**Posts on this page:** 7
**Page:** 1

<div class="post-metadata">

### Author: ![amaro](https://avatars.discourse-cdn.com/v4/letter/a/9fc348/32.png) [@amaro](https://discuss.ocaml.org/u/amaro)
#### Post date: [January 15, 2026, 10:08am UTC](https://discuss.ocaml.org/t/cram-test-errors-and-unreachable/17697/1 "2026-01-15T10:08:35Z")

</div>

Some kinds of errors in Dune Cram tests lead to a ` *****UNREACHABLE***** ` message, which makes it hard to debug what’s wrong.

For instance:

```auto
dune init proj foo
cd foo
mkdir cram.t
echo " $ source foo.sh" > cram.t/run.t
touch cram.t/foo.sh
dune runtest

```

It outputs:

```auto
File "cram.t/run.t", line 1, characters 0-0:
diff --git 1/_build/default/cram.t/run.t 2/_build/default/cram.t/run.t.corrected
index 7e8203e..84e2e4d 100644
--- 1/_build/default/cram.t/run.t
+++ 2/_build/default/cram.t/run.t.corrected
@@ -1 +1,2 @@
   $ source foo.sh
+ *****UNREACHABLE*****

```

If we add other commands to debug, such as `cat`, we see the file is visible for the shell script running it.

Another case in which UNREACHABLE appears is if we add an `exit` to `run.t`, as in:

```
  $ exit 1

```

And I think I had other cases in which it happened.

Sometimes it’s hard to find the exact cause. Is there a way to debug such scripts to get more information? Other than trying to manually emulate what Dune does, that is, executing the commands manually.

---

<div class="post-metadata">

### Author: ![dinosaure](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/dinosaure/32/16_2.png) [@dinosaure](https://discuss.ocaml.org/u/dinosaure)
#### Post date: [January 15, 2026, 10:59am UTC](https://discuss.ocaml.org/t/cram-test-errors-and-unreachable/17697/2 "2026-01-15T10:59:29Z")

</div>

> [@amaro](#):
>
> `echo " $ source foo.sh" > cram.t/run.t`

If you replace it by `source ./foo.sh`, I don’t have errors and variables are loaded (imagine `FOO=42`, I can do `echo $FOO` and it shows `42`). More generally, I prefer not to rely on the shell or tools (such as `wc`) in a cram test and prefer to recode certain utilities in OCaml (because I know that, at least, OCaml is available 🙂 ).

---

<div class="post-metadata">

### Author: ![Alizter](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/alizter/32/3735_2.png) [@Alizter](https://discuss.ocaml.org/u/Alizter)
#### Post date: [January 15, 2026, 7:49pm UTC](https://discuss.ocaml.org/t/cram-test-errors-and-unreachable/17697/3 "2026-01-15T19:49:39Z")

</div>

When a cram test says ` *****UNREACHABLE***** ` that means that the command directly above exited the shell. The question then becomes why is it that `source ./foo.sh` does not exit the shell but `source foo.sh` does?

Luckily the man page for `bash` has the answers:

```auto
       source [-p path] filename [arguments]
              The . command (source) reads and execute commands
              from filename in the current shell environment and
              returns the exit status of the last command executed
              from filename.

              If filename does not contain a slash, . searches for
              it. If the -p option is supplied, . treats path as a
              colon-separated list of directories in which to find
              filename; otherwise, . uses the entries in PATH to
              find the directory containing filename. filename
              does not need to be executable. When bash is not in
              posix mode, it searches the current directory if
              filename is not found in PATH, but does not search
              the current directory if -p is supplied. If the
              sourcepath option to the shopt builtin command is
              turned off, . does not search PATH.

```

So `source` goes and searches the whole of `PATH` to find it. When it ultimately fails to find it, it **exits** the non-interactive shell due to the fact that it is a built-in command. This is why Dune tells you that the output below is unreachable, since the test runner has exited the shell.

Requiring something to be sourced at the top of a cram test is a very common pattern. The nature of `bash` means there are a lot of footguns when it comes to writing a `source` statement as you have discovered. This is why in Dune we’ve added a new `setup_scripts` field to the `(cram)` stanza, allowing you to put `(cram (applies_to mytest) (setup_scripts foo.sh))` without any tears. This will probably appear in 3.22, but you can start using it if you look at the `main` branch or use the nightly release [https://nightly.dune.build/](https://nightly.dune.build/).

---

<div class="post-metadata">

### Author: ![SkySkimmer](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/skyskimmer/32/1672_2.png) [@SkySkimmer](https://discuss.ocaml.org/u/SkySkimmer)
#### Post date: [January 15, 2026, 8:05pm UTC](https://discuss.ocaml.org/t/cram-test-errors-and-unreachable/17697/4 "2026-01-15T20:05:10Z")

</div>

> [@Alizter](#):
>
> it **exits** the interactive shell

Is that version dependent? A failing source doesn’t exit the shell for me.

---

<div class="post-metadata">

### Author: ![yawaramin](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/yawaramin/32/3384_2.png) [@yawaramin](https://discuss.ocaml.org/u/yawaramin)
#### Post date: [January 15, 2026, 8:07pm UTC](https://discuss.ocaml.org/t/cram-test-errors-and-unreachable/17697/5 "2026-01-15T20:07:45Z")

</div>

Just a note that searching for `unreachable` in the dune docs turns up no results: [Search - Dune documentation](https://dune.readthedocs.io/en/stable/search.html?q=UNREACHABLE&check_keywords=yes&area=default)

---

<div class="post-metadata">

### Author: ![Alizter](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/alizter/32/3735_2.png) [@Alizter](https://discuss.ocaml.org/u/Alizter)
#### Post date: [January 15, 2026, 8:09pm UTC](https://discuss.ocaml.org/t/cram-test-errors-and-unreachable/17697/6 "2026-01-15T20:09:11Z")

</div>

I should have said `non-interactive` shell:

```auto
$ bash -c 'source foo.sh ; echo "foo"'
bash: line 1: foo.sh: No such file or directory
foo

$ sh -c 'source foo.sh ; echo "foo"'
sh: line 1: source: foo.sh: file not found

```

Here `sh` is typically `bash --posix` for linux users.

---

<div class="post-metadata">

### Author: ![Alizter](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/alizter/32/3735_2.png) [@Alizter](https://discuss.ocaml.org/u/Alizter)
#### Post date: [January 15, 2026, 8:12pm UTC](https://discuss.ocaml.org/t/cram-test-errors-and-unreachable/17697/7 "2026-01-15T20:12:31Z")

</div>

Thanks for the reminder, I’ll get that included as soon as possible.

Here: [doc(cram): document early shell exits by Alizter · Pull Request #13326 · ocaml/dune · GitHub](https://github.com/ocaml/dune/pull/13326)
