It seems when assertion failure is hit a portion of stack trace is optimized away.
ubuntu:~/ocaml-playground$ dune test
File "lib/Instr.ml", line 137, characters 2-71: <<true>> threw "Assert_failure lib/Util.ml:97:2".
Raised at file "src/exn.ml", line 71, characters 4-114
Called from file "runtime-lib/runtime.ml", line 356, characters 15-52
Called from file "runtime-lib/runtime.ml", line 444, characters 52-83
in TEST_MODULE at file "lib/intro.ml", line 136, characters 0-110
The printed stack trace misses a few function calls.
Missing stack frames can be due to various causes. It’s hard to pin the right one without seeing your code, but here are a few possible reasons:
catching an exception using try...with and then raising that exception again erases the first backtrace;
if a function ends in a tail call, its stack frame is reused and thus it will not appear in the stack trace;
C functions in general do not appear in OCaml backtraces.
To see a more complete backtrace, you can use a debugger, insert a breakpoint at the location of your assertion, and when it is reached fails, ask a backtrace from the debugger.
This is not always so. The compiler tries to detect when an exception is re-raised in an exception handler and it will preserve the stack trace in that case. But the heuristics are not perfect so it won’t work every time. On the other hand, it is possible to always preserve the existing backtrace by declaring
external reraise : exn -> 'a = "%reraise"
and using it to re-throw an exception in an exception handler. See also Printexc.reraise_with_backtrace.
Thank you all for comments! The reason was an infinitely recursive call. I could catch this by opening dune utop and rerunning the problematic unit test, which finally printed the full stack trace.