Once you add an error, you cannot remove that error. The DML operation is doomed to fail. However, what happens next is defined by the callee. When
allOrNone=true, the default, a DML exception is thrown, which the callee can deal with. If the callee sets
allOrNone=false, then Salesforce automatically takes care of automatic retries. This means that any client, be it Apex Code, a SOAP or REST API call, etc, can determine if partial success is an acceptable outcome.
When partial saves are allowed on a DML operation, a checkpoint is set by the system before it starts, and all records are processed. If there are no errors, the callee regains control. If there was an error, those records are set aside, the governor limits are reset back to the point where the checkpoint was set, and the remaining records are tried again. If any new errors come up, they will likewise be set aside, and a third attempt is performed. If there are any errors at this point, you get the "Too many batch retries in the presence of Apex triggers and partial failures" exception.
This behavior even works recursively. If you have a trigger that updates other records, and allows a partial save, you can then add errors back up the trigger stack to perform complicated retries with minimal effort. And, since the governor limits are reset back to the checkpoint, even if there are multiple layers, it means you'll have more governor limit time to complete your transactions, even in the face of multiple recursive errors.
In other words, it is not the callee's responsibility to remove errors, but rather the callee specifying an intent to allow partial saves. For completeness, you should always consider using
allOrNone=false in your own DML logic, and always using addError to generate errors that can lead to a partial success. It's a very clean way of dealing with errors in a consistent manner. I even once wrote an entire library around it that handled recursive partial errors.
It became handy to write things like:
Where this method would perform all the necessary logic to do partial retries and propagate errors back to the parent records for recursive retries. It doesn't even require that much code and is completely reusable.