In my past experiences with programming, I think many errors I had were because I used a dynamically typed language. These are the big problems that a dynamically typed language has for me.
I consider a compiler as an easy sanity check of my code. If something borks there, I can quickly fix the problem and move on. Whereas with a dynamically typed language, I am mainly left with two choices: use the debugger or go back through the source code. I can do these things with a typed language as well, I just catch the simple errors quicker with a compiler.
Same Work, Less Productivity
If you build a large application with a dynamically typed language, you are doing the same work as a typed language with less guarantees about the code. Whether it is documenting the types of things passed into a function, or showing an API for how to use something. IF you don’t have a computer constantly checking your results, you are leaving a lot more room for error later when your program is run (Even if you have tests you run against your code).
Harder to Encode Correct State
When I write programs nowadays, I try to force myself to write programs that can not possibly produce errors. This usually means writing more types, but doing this allows me to take advantage of the compiler to ensure that the code I write will run, and run correctly. Doing the same thing in a dynamically typed language is wasted effort, as you can only catch errors when running the program. This means having to write more tests for the specifics of your program, which is much more brittle, and less DRY.