It enables better format string checking. The problem is that failwith-like functions have fully polymorphic return types (they don't return, so they can claim to return whatever type they like), and in particular [failwith "oh no"] can act as a function and take further parameters.
Thus, if you have a failwith-like function that takes a variable number of parameters, you can easily lose the static checking that you aren't giving too many. For example, you can define a unitless failwithf:
utop # let f fmt = ksprintf (fun s -> failwith s) fmt;;
val f : ('a, unit, string, 'b) format4 -> 'a = <fun>
Now [f "%d" 4] has type ['a]. Unfortunately, this type can be unified with, say, [int -> 'b], so [f "%d" 4 5] is no longer a type error, it just raises [Failure "4"].
If, on the other hand, you define failwithf:
utop # let f fmt = ksprintf (fun s -> fun () -> failwith s) fmt;;
val f : ('a, unit, string, unit -> 'b) format4 -> 'a = <fun>
Now [f "%d" 4 5] complains that 5 doesn't match type unit. The () parameter is serving as a signpost for "end of format arguments".