One thing you can do (that Rust does) is keep a stack of opened delimiters and their positions, and pop them as you find the closing one. If you encounter a mismatch or any other parse error, you look through the open delimiters to match for indentation level or valid alternatives. The more "flag posts" or redundancy the grammar has, the more likely you are to be able to recover from malformed code. If you are in a language with a sparse grammar, recovery is less feasible.
The classic example is forgetting to end a typedef struct foo with a semicolon, at the end of an #include file.
The compiler will happily combine that with whatever follows the #include# statement in the file including it, and report an error with a line number in the file doing the #include.