I will try to answer as briefly as possible
Dynamically-typed languages in general tend to be more error prone, but Lua has problems that are not inherently caused by dynamic typing. Some of these also apply to other languages, such as JavaScript and/or Python, but Lua has a particularly bad combination of these problems, and some of them were fixed in JavaScript by the “use strict” syntax (an indication that there is a demand for avoiding these problems in programming languages). Typescript was created because of problems like these in JavaScript.
Examples:
1. Accessing an undefined variable or class member (table entry) is not an error (it returns nil). For example, if a developer mistypes the name of a variable, etc., the code will compile, and the error would be discovered only much later, possibly after spending a lot of time investigating an error caused as an indirect result of this error. Using magic strings/values is generally considered bad practice in almost any language, but almost every user-defined name in Lua is effectively a magic string/value. (Globals, probably locals, and class members are accesses to some sort of ‘table’ (map) using a string.)
It would be more useful for this to return an error, but have an explicit way of testing whether a key exists in a table (maybe by returning nil only when indexing is done using square brackets).
2. Implicit creation of variables: Variables can be assigned without being declared. Similar to point 1, this might not be what the developer intended.
3. Implicitly created variables are global. (Similar to JavaScript.) Extensive use of globals is considered a bad programming practice in general, so having the language do so by default is quite undesirable.
4. Methods can be called on an instance of an unrelated class (because passing the self-reference is explicit), i.e. even when using an instance (a table used as an object instance) to invoke a method, you can pass a different (potentially unrelated) instance as the 'self' reference. Being able to call a method on the wrong class is not a useful feature.
Of course, this is partly due to Lua’s flexibility, which enables it to be used as an OO language, while not necessarily explicitly designed as one. However, it does have syntactic sugar specifically for OO. The designers could have avoided this problem by enforcing a rule that functions declared using the `:` syntax must be called using it (thereby requiring them to be used in an OO way).
If a developer forgets that a certain method requires a self-reference (i.e. is an instance method), they would end up calling the method on what should have been the first parameter to it after the self-reference, potentially having the method modify that instance (if it is a table), and resulting in a corrupted/invalid state of it, which might be discovered only much later when it causes something else to fail (at which point, it may be difficult to determine what caused it to have that state).
5. Function parameters are not validated.
Not ensuring that they are a specified type is a common problem of dynamically typed languages, and since all parameters are optional, passing too few is not an error (though a better solution would have been to provide a syntax for indicating whether each parameter is optional), but Lua doesn’t even give an error when too many arguments are passed. This is not a result of a useful feature.
6. Lack of data hiding. (Similar to Python and JavaScript.)
7. Lack of constants. Developers have to choose between using variables or hardcoding constant values as literals (potentially in many places). The latter is more efficient, so lack of constants encourages another bad practice. This is probably done to keep the language small, but introducing them for primitive values would have no runtime overhead. (The value could be substituted when generating the bytecode).
When modifying code in such a way that its interface (how it is called) is changed, in most languages, calling code that is broken by the change would fail to compile, thus alerting the developer to what has to be changed. In Lua, the calling code will typically fail only at runtime, and not even directly cause an error then, leading to a subsequent error somewhere else.
When debugging, a proper OO structure (avoiding the problems mentioned above) narrows down where you have to look for possible causes of a bug. For example, if a field is private, you have only to check the code of its own class to see how it got a wrong value. When you don't have the ability to make it private, you can't be sure that noone on the team forgot that they weren't supposed to modify it from outside of the class, and so the error could be anywhere.
The need for compile-time checks is more important in an embedded scripting language (the intended use of Lua), in my opinion, because it is more likely that it will be used by inexperienced developers.
It is even more important when used for scripting games, since games tend to be more difficult to debug.
Also, it has some unusual and inconsistent operator names:
The unary and binary `~` operators have different meanings (bitwise 'not' and 'xor'), and don't appear to be related to the use of "~" outside of Lua (for example, it can mean 'approximate');
`~=` (for inequality) resembles the general 'approximately equal' symbol;
`//` (integer division) is a comment in C++-based languages.
As others have pointed out, it lacks features of other languages, and has a smaller standard library, but this has advantages too, making it small and efficient.
The small size of the language specification is also an advantage for a scripting language, since it means that users can learn it quickly, but its error-proneness makes it particularly unsuited to beginners.
Some features are bad for performance: accessing almost anything requires a table lookup (but dynamic language interpreter implementations are likely to involve a lot of lookups anyway, and it's designer to be implemented as an interpreter); lack of more efficient data types (though version 5.3 introduces integers), for example you can't use double precision where you need it and still use single precision in other places. But this makes it simpler, and it achieves quite good performance anyway (when comparing Lua interpreters to other interpreters and Lua compilers to other compilers).