Repository: omni-us/jsonargparse
Pull request #231 (2023-01-16 19:33:47)

Deprecate ArgumentParser.error_handler in favour of exit_on_error
## What does this PR do?

- ArgumentParser.error_handler is now deprecated and will be removed in v5.0.0.
- When parsing fails now argparse.ArgumentError is raised instead of ParserError.

## Before submitting

- [x] Did you read the [contributing guideline](https://github.com/omni-us/jsonargparse/blob/master/CONTRIBUTING.rst)?
- [x] Did you update **the documentation**? (readme and public docstrings)
- [x] Did you write **unit tests** such that there is 100% coverage on related code? (required for bug fixes and new features)
- [x] Did you verify that new and existing **tests pass locally**?
- [x] Did you make sure that all changes preserve **backward compatibility**?
- [x] Did you update **the CHANGELOG**? (not for typos, docs, test updates, or minor internal changes/refactors)


sonarcloud[bot]: Kudos, SonarCloud Quality Gate passed!&nbsp; &nbsp; [![Quality Gate passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/passed-16px.png 'Quality Gate passed')](https://sonarcloud.io/dashboard?id=omni-us_jsonargparse&pullRequest=231)

[![Bug](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug-16px.png 'Bug')](https://sonarcloud.io/project/issues?id=omni-us_jsonargparse&pullRequest=231&resolved=false&types=BUG) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=omni-us_jsonargparse&pullRequest=231&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=omni-us_jsonargparse&pullRequest=231&resolved=false&types=BUG)  
[![Vulnerability](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability-16px.png 'Vulnerability')](https://sonarcloud.io/project/issues?id=omni-us_jsonargparse&pullRequest=231&resolved=false&types=VULNERABILITY) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=omni-us_jsonargparse&pullRequest=231&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=omni-us_jsonargparse&pullRequest=231&resolved=false&types=VULNERABILITY)  
[![Security Hotspot](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot-16px.png 'Security Hotspot')](https://sonarcloud.io/project/security_hotspots?id=omni-us_jsonargparse&pullRequest=231&resolved=false&types=SECURITY_HOTSPOT) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/security_hotspots?id=omni-us_jsonargparse&pullRequest=231&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=omni-us_jsonargparse&pullRequest=231&resolved=false&types=SECURITY_HOTSPOT)  
[![Code Smell](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell-16px.png 'Code Smell')](https://sonarcloud.io/project/issues?id=omni-us_jsonargparse&pullRequest=231&resolved=false&types=CODE_SMELL) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=omni-us_jsonargparse&pullRequest=231&resolved=false&types=CODE_SMELL) [0 Code Smells](https://sonarcloud.io/project/issues?id=omni-us_jsonargparse&pullRequest=231&resolved=false&types=CODE_SMELL)

[![No Coverage information](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/NoCoverageInfo-16px.png 'No Coverage information')](https://sonarcloud.io/component_measures?id=omni-us_jsonargparse&pullRequest=231) No Coverage information  
[![0.0%](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/3-16px.png '0.0%')](https://sonarcloud.io/component_measures?id=omni-us_jsonargparse&pullRequest=231&metric=new_duplicated_lines_density&view=list) [0.0% Duplication](https://sonarcloud.io/component_measures?id=omni-us_jsonargparse&pullRequest=231&metric=new_duplicated_lines_density&view=list)


codecov-commenter: # [Codecov](https://codecov.io/gh/omni-us/jsonargparse/pull/231?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=omni-us) Report
Base: **100.00**% // Head: **100.00**% // No change to project coverage :thumbsup:
> Coverage data is based on head [(`0f03565`)](https://codecov.io/gh/omni-us/jsonargparse/pull/231?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=omni-us) compared to base [(`2051bc6`)](https://codecov.io/gh/omni-us/jsonargparse/commit/2051bc61787fda8369c6f7190e19d9f02b963a38?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=omni-us).
> Patch coverage: 100.00% of modified lines in pull request are covered.

<details><summary>Additional details and impacted files</summary>


```diff
@@            Coverage Diff            @@
##            master      #231   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files           20        20           
  Lines         5162      5183   +21     
=========================================
+ Hits          5162      5183   +21     
```

| Flag | Coverage Δ | |
|---|---|---|
| py3.10 | `87.92% <98.38%> (+0.04%)` | :arrow_up: |
| py3.10_all | `99.88% <100.00%> (+<0.01%)` | :arrow_up: |
| py3.6 | `87.56% <98.36%> (+0.04%)` | :arrow_up: |
| py3.6_all | `99.63% <100.00%> (+<0.01%)` | :arrow_up: |
| py3.7 | `87.58% <98.36%> (+0.04%)` | :arrow_up: |
| py3.7_all | `99.80% <100.00%> (+<0.01%)` | :arrow_up: |
| py3.8 | `87.90% <98.38%> (+0.04%)` | :arrow_up: |
| py3.8_all | `99.84% <100.00%> (+<0.01%)` | :arrow_up: |
| py3.9 | `87.90% <98.38%> (+0.04%)` | :arrow_up: |
| py3.9_all | `99.86% <100.00%> (+<0.01%)` | :arrow_up: |

Flags with carried forward coverage won't be shown. [Click here](https://docs.codecov.io/docs/carryforward-flags?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=omni-us#carryforward-flags-in-the-pull-request-comment) to find out more.

| [Impacted Files](https://codecov.io/gh/omni-us/jsonargparse/pull/231?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=omni-us) | Coverage Δ | |
|---|---|---|
| [jsonargparse/\_\_init\_\_.py](https://codecov.io/gh/omni-us/jsonargparse/pull/231?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=omni-us#diff-anNvbmFyZ3BhcnNlL19faW5pdF9fLnB5) | `100.00% <100.00%> (ø)` | |
| [jsonargparse/actions.py](https://codecov.io/gh/omni-us/jsonargparse/pull/231?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=omni-us#diff-anNvbmFyZ3BhcnNlL2FjdGlvbnMucHk=) | `100.00% <100.00%> (ø)` | |
| [jsonargparse/core.py](https://codecov.io/gh/omni-us/jsonargparse/pull/231?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=omni-us#diff-anNvbmFyZ3BhcnNlL2NvcmUucHk=) | `100.00% <100.00%> (ø)` | |
| [jsonargparse/deprecated.py](https://codecov.io/gh/omni-us/jsonargparse/pull/231?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=omni-us#diff-anNvbmFyZ3BhcnNlL2RlcHJlY2F0ZWQucHk=) | `100.00% <100.00%> (ø)` | |
| [jsonargparse/jsonnet.py](https://codecov.io/gh/omni-us/jsonargparse/pull/231?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=omni-us#diff-anNvbmFyZ3BhcnNlL2pzb25uZXQucHk=) | `100.00% <100.00%> (ø)` | |
| [jsonargparse/typehints.py](https://codecov.io/gh/omni-us/jsonargparse/pull/231?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=omni-us#diff-anNvbmFyZ3BhcnNlL3R5cGVoaW50cy5weQ==) | `100.00% <100.00%> (ø)` | |
| [jsonargparse/util.py](https://codecov.io/gh/omni-us/jsonargparse/pull/231?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=omni-us#diff-anNvbmFyZ3BhcnNlL3V0aWwucHk=) | `100.00% <100.00%> (ø)` | |

Help us with your feedback. Take ten seconds to tell us [how you rate us](https://about.codecov.io/nps?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=omni-us). Have a feature suggestion? [Share it here.](https://app.codecov.io/gh/feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=omni-us)

</details>

[:umbrella: View full report at Codecov](https://codecov.io/gh/omni-us/jsonargparse/pull/231?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=omni-us).   
:loudspeaker: Do you have feedback about the report comment? [Let us know in this issue](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=omni-us).


Patch:
diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index efeae4d..0aef214 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -15,6 +15,11 @@ paths are considered internals and can change in minor and patch releases.
 v4.20.0 (2023-01-??)
 --------------------
 
+Changed
+^^^^^^^
+- When parsing fails now ``argparse.ArgumentError`` is raised instead of
+  ``ParserError``.
+
 Deprecated
 ^^^^^^^^^^
 - Path ``skip_check`` parameter is deprecated and will be removed in v5.0.0.
@@ -24,6 +29,8 @@ Deprecated
   absolute``.
 - ``ActionPathList`` is deprecated and will be removed in v5.0.0. Instead use as
   type ``List[<path_type>]`` with ``enable_path=True``.
+- ``ArgumentParser.error_handler`` is deprecated and will be removed in v5.0.0.
+  Instead use the new exit_on_error parameter from argparse.
 
 
 v4.19.0 (2022-12-27)
diff --git a/README.rst b/README.rst
index cc0fc70..f2d6247 100644
--- a/README.rst
+++ b/README.rst
@@ -318,7 +318,7 @@ shown above you would observe:
 
 If the parsing fails the standard behavior is that the usage is printed and the
 program is terminated. Alternatively you can initialize the parser with
-``error_handler=None`` in which case a :class:`.ParserError` is raised.
+``exit_on_error=False`` in which case an :class:`.ArgumentError` is raised.
 
 
 Override order
diff --git a/jsonargparse/__init__.py b/jsonargparse/__init__.py
index e36e282..95c5589 100644
--- a/jsonargparse/__init__.py
+++ b/jsonargparse/__init__.py
@@ -1,4 +1,12 @@
-from argparse import ONE_OR_MORE, OPTIONAL, PARSER, REMAINDER, SUPPRESS, ZERO_OR_MORE
+from argparse import (
+    ONE_OR_MORE,
+    OPTIONAL,
+    PARSER,
+    REMAINDER,
+    SUPPRESS,
+    ZERO_OR_MORE,
+    ArgumentError,
+)
 
 from .actions import *
 from .cli import *
@@ -16,6 +24,7 @@ from .typehints import *
 from .util import *
 
 __all__ = [
+    'ArgumentError',
     'OPTIONAL',
     'REMAINDER',
     'SUPPRESS',
diff --git a/jsonargparse/actions.py b/jsonargparse/actions.py
index 1843a1f..dd3b5a1 100644
--- a/jsonargparse/actions.py
+++ b/jsonargparse/actions.py
@@ -18,8 +18,8 @@ from .type_checking import ArgumentParser
 from .util import (
     LoggerProperty,
     NoneType,
-    ParserError,
     Path,
+    argument_error,
     change_to_path_dir,
     default_config_option_help,
     get_typehint_origin,
@@ -246,7 +246,7 @@ class _ActionPrintConfig(Action):
             flags = value[0].split(',')
             invalid_flags = [f for f in flags if f not in valid_flags]
             if len(invalid_flags) > 0:
-                raise ParserError(f'Invalid option "{invalid_flags[0]}" for {option_string}')
+                raise argument_error(f'Invalid option "{invalid_flags[0]}" for {option_string}')
             for flag in [f for f in flags if f != '']:
                 kwargs[valid_flags[flag]] = True
         while hasattr(parser, 'parent_parser'):
@@ -383,7 +383,7 @@ class _ActionHelpClassPath(Action):
         args = self.get_args_after_opt(parser.args)
         if args:
             subparser.parse_args(args)
-            raise ParserError(f'Expected a nested --*.help option, got: {args}.')
+            raise argument_error(f'Expected a nested --*.help option, got: {args}.')
         else:
             subparser.print_help()
             parser.exit()
@@ -598,7 +598,8 @@ class _ActionSubCommands(_SubParsersAction):
         parser.default_env = self.parent_parser.default_env
         parser.parent_parser = self.parent_parser
         parser.parser_mode = self.parent_parser.parser_mode
-        parser.error_handler = self.parent_parser.error_handler
+        parser._error_handler = self.parent_parser._error_handler
+        parser.exit_on_error = self.parent_parser.exit_on_error
         parser.logger = self.parent_parser.logger
         parser.subcommand = name
 
diff --git a/jsonargparse/core.py b/jsonargparse/core.py
index 7bf311d..a951acb 100644
--- a/jsonargparse/core.py
+++ b/jsonargparse/core.py
@@ -39,6 +39,7 @@ from .actions import (
     parent_parsers,
     previous_config,
 )
+from .deprecated import ParserDeprecations
 from .formatters import DefaultHelpFormatter, empty_help, get_env_var
 from .jsonnet import ActionJsonnet, ActionJsonnetExtVars
 from .jsonschema import ActionJsonSchema
@@ -74,14 +75,13 @@ from .signatures import SignatureArguments, is_pure_dataclass
 from .typehints import ActionTypeHint, is_subclass_spec
 from .typing import is_final_class
 from .util import (
-    ParserError,
     Path,
+    argument_error,
     change_to_path_dir,
     get_private_kwargs,
     identity,
     is_subclass,
     return_parser_if_captured,
-    usage_and_exit_error_handler,
 )
 
 __all__ = ['ActionsContainer', 'ArgumentParser']
@@ -183,7 +183,7 @@ class _ArgumentGroup(ActionsContainer, argparse._ArgumentGroup):
     parser: Optional['ArgumentParser'] = None
 
 
-class ArgumentParser(ActionsContainer, ArgumentLinking, argparse.ArgumentParser):
+class ArgumentParser(ParserDeprecations, ActionsContainer, ArgumentLinking, argparse.ArgumentParser):
     """Parser for command line, yaml/jsonnet files and environment variables."""
 
     formatter_class: Type[DefaultHelpFormatter]
@@ -195,8 +195,8 @@ class ArgumentParser(ActionsContainer, ArgumentLinking, argparse.ArgumentParser)
         self,
         *args,
         env_prefix: Union[bool, str] = True,
-        error_handler: Optional[Callable[['ArgumentParser', str], None]] = usage_and_exit_error_handler,
         formatter_class: Type[DefaultHelpFormatter] = DefaultHelpFormatter,
+        exit_on_error: bool = True,
         logger: Union[bool, str, dict, logging.Logger] = False,
         version: Optional[str] = None,
         print_config: Optional[str] = '--print_config',
@@ -215,7 +215,6 @@ class ArgumentParser(ActionsContainer, ArgumentLinking, argparse.ArgumentParser)
 
         Args:
             env_prefix: Prefix for environment variables. ``True`` to derive from ``prog``.
-            error_handler: Handler for parsing errors, if None raises :class:`.ParserError` exception.
             formatter_class: Class for printing help messages.
             logger: Configures the logger, see :class:`.LoggerProperty`.
             version: Program version which will be printed by the --version argument.
@@ -229,6 +228,7 @@ class ArgumentParser(ActionsContainer, ArgumentLinking, argparse.ArgumentParser)
         super().__init__(*args, formatter_class=formatter_class, logger=logger, **kwargs)
         if self.groups is None:
             self.groups = {}
+        self.exit_on_error = exit_on_error
         self.required_args: Set[str] = set()
         self.save_path_content: Set[str] = set()
         self.default_config_files = default_config_files  # type: ignore
@@ -237,7 +237,6 @@ class ArgumentParser(ActionsContainer, ArgumentLinking, argparse.ArgumentParser)
         self.env_prefix = env_prefix
         self.parser_mode = parser_mode
         self.dump_header = dump_header
-        self.error_handler = error_handler
         self._print_config = print_config
         if version is not None:
             self.add_argument('--version', action='version', version='%(prog)s '+version, help='Print version and exit.')
@@ -257,7 +256,7 @@ class ArgumentParser(ActionsContainer, ArgumentLinking, argparse.ArgumentParser)
         try:
             with patch_namespace(), parser_context(parent_parser=self, lenient_check=True), ActionTypeHint.subclass_arg_context(self):
                 namespace, args = self._parse_known_args(args, namespace)
-        except (argparse.ArgumentError, ParserError) as ex:
+        except argparse.ArgumentError as ex:
             self.error(str(ex), ex)
 
         return namespace, args
@@ -367,7 +366,7 @@ class ArgumentParser(ActionsContainer, ArgumentLinking, argparse.ArgumentParser)
             A config object with all parsed values.
 
         Raises:
-            ParserError: If there is a parsing error and error_handler=None.
+            ArgumentError: If the parsing fails error and exit_on_error=True.
         """
         skip_check = get_private_kwargs(kwargs, _skip_check=False)
         return_parser_if_captured(self)
@@ -427,7 +426,7 @@ class ArgumentParser(ActionsContainer, ArgumentLinking, argparse.ArgumentParser)
             A config object with all parsed values.
 
         Raises:
-            ParserError: If there is a parsing error and error_handler=None.
+            ArgumentError: If the parsing fails error and exit_on_error=True.
         """
         skip_check, skip_required = get_private_kwargs(kwargs, _skip_check=False, _skip_required=False)
 
@@ -506,7 +505,7 @@ class ArgumentParser(ActionsContainer, ArgumentLinking, argparse.ArgumentParser)
             A config object with all parsed values.
 
         Raises:
-            ParserError: If there is a parsing error and error_handler=None.
+            ArgumentError: If the parsing fails error and exit_on_error=True.
         """
         skip_check, skip_subcommands = get_private_kwargs(kwargs, _skip_check=False, _skip_subcommands=False)
 
@@ -551,7 +550,7 @@ class ArgumentParser(ActionsContainer, ArgumentLinking, argparse.ArgumentParser)
             A config object with all parsed values.
 
         Raises:
-            ParserError: If there is a parsing error and error_handler=None.
+            ArgumentError: If the parsing fails error and exit_on_error=True.
         """
         fpath = Path(cfg_path, mode=get_config_read_mode())
         with change_to_path_dir(fpath):
@@ -594,7 +593,7 @@ class ArgumentParser(ActionsContainer, ArgumentLinking, argparse.ArgumentParser)
             A config object with all parsed values.
 
         Raises:
-            ParserError: If there is a parsing error and error_handler=None.
+            ArgumentError: If the parsing fails error and exit_on_error=True.
         """
         skip_check, fail_no_subcommand = get_private_kwargs(kwargs, _skip_check=False, _fail_no_subcommand=True)
 
@@ -981,8 +980,8 @@ class ArgumentParser(ActionsContainer, ArgumentLinking, argparse.ArgumentParser)
                             skip_check=skip_check,
                             skip_required=True,
                         )
-                except (TypeError, KeyError, ParserError) as ex:
-                    raise ParserError(f'Problem in default config file "{default_config_file}" :: {ex.args[0]}') from ex
+                except (TypeError, KeyError, argparse.ArgumentError) as ex:
+                    raise argument_error(f'Problem in default config file "{default_config_file}": {ex.args[0]}') from ex
             meta = cfg.get('__default_config__')
             if isinstance(meta, list):
                 meta.append(default_config_file)
@@ -1000,14 +999,19 @@ class ArgumentParser(ActionsContainer, ArgumentLinking, argparse.ArgumentParser)
     ## Other methods ##
 
     def error(self, message: str, ex: Optional[Exception] = None) -> NoReturn:
-        """Logs error message if a logger is set, calls the error handler and raises a ParserError."""
+        """Logs error message if a logger is set and exits or raises an ArgumentError."""
         self._logger.error(message)
-        if self._error_handler is not None:
+        if callable(self._error_handler):
             self._error_handler(self, message)
-        if ex is None:
-            raise ParserError(message)
-        else:
-            raise ParserError(message) from ex
+        if not self.exit_on_error:
+            raise argument_error(message) from ex
+        elif 'JSONARGPARSE_DEBUG' in os.environ:
+            self._logger.debug('Debug enabled, thus raising exception instead of exit.')
+            raise argument_error(message) from ex
+        self.print_usage(sys.stderr)
+        args = {'prog': self.prog, 'message': message}
+        sys.stderr.write('%(prog)s: error: %(message)s\n' % args)
+        self.exit(2)
 
 
     def check_config(
@@ -1193,7 +1197,7 @@ class ArgumentParser(ActionsContainer, ArgumentLinking, argparse.ArgumentParser)
                     if isinstance(config_files, list):
                         config_files = [str(x) for x in config_files]
                     note = f'default values below are the ones overridden by the contents of: {config_files}'
-            except ParserError as ex:
+            except argparse.ArgumentError as ex:
                 note = f'tried getting defaults considering default_config_files but failed due to: {ex}'
             group = self._default_config_files_group
             group.description = f'{self._default_config_files}, Note: {note}'
@@ -1327,27 +1331,6 @@ class ArgumentParser(ActionsContainer, ArgumentLinking, argparse.ArgumentParser)
 
     ## Properties ##
 
-    @property
-    def error_handler(self) -> Optional[Callable[['ArgumentParser', str], None]]:
-        """Property for the error_handler function that is called when there are parsing errors.
-
-        :getter: Returns the current error_handler function.
-        :setter: Sets a new error_handler function (Callable[self, message:str] or None).
-
-        Raises:
-            ValueError: If an invalid value is given.
-        """
-        return self._error_handler
-
-
-    @error_handler.setter
-    def error_handler(self, error_handler: Optional[Callable[['ArgumentParser', str], None]]):
-        if callable(error_handler) or error_handler is None:
-            self._error_handler = error_handler
-        else:
-            raise ValueError('error_handler can be either a Callable or None.')
-
-
     @property
     def default_config_files(self) -> List[str]:
         """Default config file locations.
diff --git a/jsonargparse/deprecated.py b/jsonargparse/deprecated.py
index 1ea9b79..bce5e12 100644
--- a/jsonargparse/deprecated.py
+++ b/jsonargparse/deprecated.py
@@ -4,19 +4,22 @@ import functools
 import inspect
 import os
 import sys
-from argparse import Action
+from argparse import Action, ArgumentError
 from enum import Enum
-from typing import Any, Dict, Optional, Set
+from typing import Any, Callable, Dict, Optional, Set
 
 from .namespace import Namespace
 from .optionals import FilesCompleterMethod
+from .type_checking import ArgumentParser
 
 __all__ = [
     'ActionEnum',
     'ActionOperators',
     'ActionPath',
     'ActionPathList',
+    'ParserError',
     'set_url_support',
+    'usage_and_exit_error_handler',
 ]
 
 
@@ -380,7 +383,6 @@ path_immutable_attrs_message = """
     Please update your code to use the new property names and don't modify path
     attributes. The changes are: ``rel_path`` -> ``relative`` and ``abs_path``
     -> ``absolute``, ``cwd`` no name change, ``skip_check`` will be removed.
-
 """
 
 class PathDeprecations:
@@ -435,6 +437,69 @@ class PathDeprecations:
         self._skip_check = skip_check
 
 
+class DebugException(Exception):
+    pass
+
+
+@deprecated("""
+    usage_and_exit_error_handler was deprecated in v4.20.0 and will be removed
+    in v5.0.0. With the removal of error_handler, there is no longer a need for
+    this function.
+""")
+def usage_and_exit_error_handler(parser: 'ArgumentParser', message: str) -> None:
+    """Prints the usage and exits with error code 2 (same behavior as argparse).
+
+    Args:
+        parser: The parser object.
+        message: The message describing the error being handled.
+    """
+    parser.print_usage(sys.stderr)
+    args = {'prog': parser.prog, 'message': message}
+    sys.stderr.write('%(prog)s: error: %(message)s\n' % args)
+    parser.exit(2)
+
+
+error_handler_message = """
+    ArgumentParser's error_handler was deprecated in v4.20.0 and will be removed
+    in v5.0.0. Instead use the new exit_on_error parameter from argparse.
+"""
+
+
+def deprecation_warning_error_handler():
+    deprecation_warning('ArgumentParser.error_handler', error_handler_message)
+
+
+class ParserDeprecations:
+
+    def __init__(self, *args, error_handler=False, **kwargs):
+        super().__init__(*args, **kwargs)
+        self.error_handler = error_handler
+
+    @property
+    def error_handler(self) -> Optional[Callable[['ArgumentParser', str], None]]:
+        """Property for the error_handler function that is called when there are parsing errors.
+
+        :getter: Returns the current error_handler function.
+        :setter: Sets a new error_handler function (Callable[self, message:str] or None).
+
+        Raises:
+            ValueError: If an invalid value is given.
+        """
+        return self._error_handler
+
+    @error_handler.setter
+    def error_handler(self, error_handler):
+        if error_handler is not False:
+            deprecation_warning_error_handler()
+        if callable(error_handler) or error_handler in {None, False}:
+            self._error_handler = error_handler
+        else:
+            raise ValueError('error_handler can be either a Callable or None.')
+
+
+ParserError = ArgumentError
+
+
 import jsonargparse.optionals
 
 jsonargparse.optionals.import_docstring_parse = import_docstring_parse  # type: ignore
diff --git a/jsonargparse/jsonnet.py b/jsonargparse/jsonnet.py
index 5d4eefe..190cc92 100644
--- a/jsonargparse/jsonnet.py
+++ b/jsonargparse/jsonnet.py
@@ -12,7 +12,7 @@ from .optionals import (
     import_jsonnet,
     import_jsonschema,
 )
-from .util import ParserError, Path
+from .util import Path, argument_error
 
 __all__ = [
     'ActionJsonnetExtVars',
@@ -166,7 +166,7 @@ class ActionJsonnet(Action):
             with parser_context(load_value_mode='yaml'):
                 values = load_value(_jsonnet.evaluate_snippet(fname, snippet, ext_vars=ext_vars, ext_codes=ext_codes))
         except RuntimeError as ex:
-            raise ParserError(f'Problems evaluating jsonnet "{fname}" :: {ex}') from ex
+            raise argument_error(f'Problems evaluating jsonnet "{fname}": {ex}') from ex
         if self._validator is not None:
             self._validator.validate(values)
         if with_meta:
diff --git a/jsonargparse/typehints.py b/jsonargparse/typehints.py
index db7c907..76d4ad7 100644
--- a/jsonargparse/typehints.py
+++ b/jsonargparse/typehints.py
@@ -4,6 +4,7 @@ import inspect
 import os
 import re
 import sys
+from argparse import ArgumentError
 from collections import abc, defaultdict
 from contextlib import contextmanager, suppress
 from contextvars import ContextVar
@@ -59,7 +60,6 @@ from .util import (
     ClassType,
     NestedArg,
     NoneType,
-    ParserError,
     Path,
     change_to_path_dir,
     get_import_path,
@@ -484,7 +484,7 @@ class ActionTypeHint(Action):
         if skip_args:
             kwargs.setdefault('skip', set()).add(skip_args)
         parser = parent_parser.get()
-        parser = type(parser)(error_handler=None, logger=parser.logger)
+        parser = type(parser)(exit_on_error=False, logger=parser.logger)
         remove_actions(parser, (ActionConfigFile, _ActionPrintConfig))
         parser.add_class_arguments(val_class, **kwargs)
         return parser
@@ -754,7 +754,7 @@ def adapt_typehints(
                         raise ImportError(f'Expected {val["class_path"]!r} to be a class that instantiates into callable or a subclass of {partial_classes}')
                     val['class_path'] = get_import_path(val_class)
                     val = adapt_class_type(val, False, instantiate_classes, sub_add_kwargs, skip_args=num_partial_args)
-            except (ImportError, AttributeError, ParserError) as ex:
+            except (ImportError, AttributeError, ArgumentError) as ex:
                 raise_unexpected_value(f'Type {typehint} expects a function or a callable class: {ex}', val, ex)
 
     # Subclass
@@ -783,7 +783,7 @@ def adapt_typehints(
                 raise_unexpected_value(f'Import path {val["class_path"]} does not correspond to a subclass of {typehint}')
             val['class_path'] = get_import_path(val_class)
             val = adapt_class_type(val, serialize, instantiate_classes, sub_add_kwargs, prev_val=prev_val)
-        except (ImportError, AttributeError, AssertionError, ParserError) as ex:
+        except (ImportError, AttributeError, AssertionError, ArgumentError) as ex:
             class_path = val if isinstance(val, str) else val['class_path']
             error = indent_text(f'\n- {ex}')
             raise_unexpected_value(f'Problem with given class_path {class_path!r}:{error}', exception=ex)
@@ -1143,11 +1143,11 @@ def callable_instances(cls: Type):
 def check_lazy_kwargs(class_type: Type, lazy_kwargs: dict):
     if lazy_kwargs:
         from .core import ArgumentParser
-        parser = ArgumentParser(error_handler=None)
+        parser = ArgumentParser(exit_on_error=False)
         parser.add_class_arguments(class_type)
         try:
             parser.parse_object(lazy_kwargs)
-        except ParserError as ex:
+        except ArgumentError as ex:
             raise ValueError(str(ex)) from ex
 
 
diff --git a/jsonargparse/util.py b/jsonargparse/util.py
index 4d56a33..dc88446 100644
--- a/jsonargparse/util.py
+++ b/jsonargparse/util.py
@@ -8,6 +8,7 @@ import stat
 import sys
 import textwrap
 import warnings
+from argparse import ArgumentError
 from collections import Counter, namedtuple
 from contextlib import contextmanager
 from contextvars import ContextVar
@@ -48,10 +49,8 @@ __all__ = [
     'class_from_function',
     'LoggerProperty',
     'null_logger',
-    'ParserError',
     'Path',
     'register_unresolvable_import_paths',
-    'usage_and_exit_error_handler',
 ]
 
 
@@ -73,12 +72,8 @@ class UrlData:
     url_path: str
 
 
-class ParserError(Exception):
-    """Error raised when parsing a value fails."""
-
-
-class DebugException(Exception):
-    pass
+def argument_error(message: str) -> ArgumentError:
+    return ArgumentError(None, message)
 
 
 class JsonargparseWarning(UserWarning):
@@ -159,25 +154,6 @@ def parse_value_or_config(value: Any, enable_path: bool = True, simple_types: bo
     return value, cfg_path
 
 
-def usage_and_exit_error_handler(parser: 'ArgumentParser', message: str) -> None:
-    """Error handler that prints the usage and exits with error code 2 (same behavior as argparse).
-
-    If the JSONARGPARSE_DEBUG environment variable is set, instead of exit, a
-    DebugException is raised.
-
-    Args:
-        parser: The parser object.
-        message: The message describing the error being handled.
-    """
-    parser.print_usage(sys.stderr)
-    args = {'prog': parser.prog, 'message': message}
-    sys.stderr.write('%(prog)s: error: %(message)s\n' % args)
-    if 'JSONARGPARSE_DEBUG' in os.environ:
-        raise DebugException('jsonargparse debug enabled, thus raising exception instead of exit.')
-    else:
-        parser.exit(2)
-
-
 class CachedStdin(StringIO):
     """Used to allow reading sys.stdin multiple times."""
 


Test patch:
diff --git a/jsonargparse_tests/test_actions.py b/jsonargparse_tests/test_actions.py
index c90b01b..056bb13 100755
--- a/jsonargparse_tests/test_actions.py
+++ b/jsonargparse_tests/test_actions.py
@@ -9,8 +9,8 @@ from jsonargparse import (
     ActionConfigFile,
     ActionParser,
     ActionYesNo,
+    ArgumentError,
     ArgumentParser,
-    ParserError,
     strip_meta,
 )
 from jsonargparse_tests.base import TempDirTestCase
@@ -33,7 +33,7 @@ class SimpleActionsTests(unittest.TestCase):
         self.assertEqual(True,  parser.parse_args(['--bools.def_false=yes']).bools.def_false)
         self.assertEqual(False, parser.parse_args(['--bools.def_false=no']).bools.def_false)
         self.assertEqual(True,  parser.parse_args(['--no_bools.def_true=no']).bools.def_true)
-        self.assertRaises(ParserError, lambda: parser.parse_args(['--bools.def_true nope']))
+        self.assertRaises(ArgumentError, lambda: parser.parse_args(['--bools.def_true nope']))
 
         parser = ArgumentParser()
         parser.add_argument('--val', action=ActionYesNo)
@@ -62,14 +62,14 @@ class SimpleActionsTests(unittest.TestCase):
 
 
     def test_ActionYesNo_old_bool(self):
-        parser = ArgumentParser(error_handler=None)
+        parser = ArgumentParser(exit_on_error=False)
         parser.add_argument('--val', nargs=1, action=ActionYesNo(no_prefix=None))
         self.assertEqual(False, parser.get_defaults().val)
         self.assertEqual(True,  parser.parse_args(['--val', 'true']).val)
         self.assertEqual(True,  parser.parse_args(['--val', 'yes']).val)
         self.assertEqual(False, parser.parse_args(['--val', 'false']).val)
         self.assertEqual(False, parser.parse_args(['--val', 'no']).val)
-        self.assertRaises(ParserError, lambda: parser.parse_args(['--val', '1']))
+        self.assertRaises(ArgumentError, lambda: parser.parse_args(['--val', '1']))
 
 
 class ActionParserTests(TempDirTestCase):
@@ -85,7 +85,7 @@ class ActionParserTests(TempDirTestCase):
         parser_lv2.add_argument('--inner3',
             action=ActionParser(parser=parser_lv3))
 
-        parser = ArgumentParser(prog='lv1', default_env=True, error_handler=None)
+        parser = ArgumentParser(prog='lv1', default_env=True, exit_on_error=False)
         parser.add_argument('--opt1',
             default='opt1_def')
         parser.add_argument('--inner2',
@@ -169,22 +169,22 @@ class ActionParserTests(TempDirTestCase):
     def test_ActionParser_required(self):
         p1 = ArgumentParser()
         p1.add_argument('--op1', required=True)
-        p2 = ArgumentParser(error_handler=None)
+        p2 = ArgumentParser(exit_on_error=False)
         p2.add_argument('--op2', action=ActionParser(parser=p1))
         p2.parse_args(['--op2.op1=1'])
-        self.assertRaises(ParserError, lambda: p2.parse_args([]))
+        self.assertRaises(ArgumentError, lambda: p2.parse_args([]))
 
 
     def test_ActionParser_failures(self):
         parser_lv2 = ArgumentParser()
         parser_lv2.add_argument('--op')
-        parser = ArgumentParser(error_handler=None)
+        parser = ArgumentParser(exit_on_error=False)
         parser.add_argument('--inner', action=ActionParser(parser=parser_lv2))
         self.assertRaises(Exception, lambda: parser.add_argument('--bad', action=ActionParser))
         self.assertRaises(ValueError, lambda: parser.add_argument('--bad', action=ActionParser(parser=parser)))
         self.assertRaises(ValueError, lambda: parser.add_argument('--bad', type=str, action=ActionParser(ArgumentParser())))
         self.assertRaises(ValueError, lambda: parser.add_argument('-b', '--bad', action=ActionParser(ArgumentParser())))
-        self.assertRaises(ParserError, lambda: parser.parse_args(['--inner=1']))
+        self.assertRaises(ArgumentError, lambda: parser.parse_args(['--inner=1']))
         self.assertRaises(ValueError, lambda: ActionParser())
         self.assertRaises(ValueError, lambda: ActionParser(parser=object))
 
@@ -192,21 +192,21 @@ class ActionParserTests(TempDirTestCase):
     def test_ActionParser_conflict(self):
         parser_lv2 = ArgumentParser()
         parser_lv2.add_argument('--op')
-        parser = ArgumentParser(error_handler=None)
+        parser = ArgumentParser(exit_on_error=False)
         parser.add_argument('--inner.op')
         self.assertRaises(ValueError, lambda: parser.add_argument('--inner', action=ActionParser(parser_lv2)))
 
 
     def test_ActionParser_nested_dash_names(self):
-        p1 = ArgumentParser(error_handler=None)
+        p1 = ArgumentParser(exit_on_error=False)
         p1.add_argument('--op1-like')
 
-        p2 = ArgumentParser(error_handler=None)
+        p2 = ArgumentParser(exit_on_error=False)
         p2.add_argument('--op2-like', action=ActionParser(parser=p1))
 
         self.assertEqual(p2.parse_args(['--op2-like.op1-like=a']).op2_like.op1_like, 'a')
 
-        p3 = ArgumentParser(error_handler=None)
+        p3 = ArgumentParser(exit_on_error=False)
         p3.add_argument('--op3', action=ActionParser(parser=p2))
 
         self.assertEqual(p3.parse_args(['--op3.op2-like.op1-like=b']).op3.op2_like.op1_like, 'b')
diff --git a/jsonargparse_tests/test_argcomplete.py b/jsonargparse_tests/test_argcomplete.py
index b45a51e..a69bc3e 100755
--- a/jsonargparse_tests/test_argcomplete.py
+++ b/jsonargparse_tests/test_argcomplete.py
@@ -42,7 +42,7 @@ class ArgcompleteTests(TempDirTestCase):
         os.environ['_ARGCOMPLETE_SUPPRESS_SPACE'] = '1'
         os.environ['_ARGCOMPLETE_COMP_WORDBREAKS'] = " \t\n\"'><=;|&(:"
         os.environ['COMP_TYPE'] = str(ord('?'))   # ='63'  str(ord('\t'))='9'
-        self.parser = ArgumentParser(error_handler=lambda x: x.exit(2))
+        self.parser = ArgumentParser()
         stack = ExitStack()
         stack.enter_context(parser_context(load_value_mode='yaml'))
         self.addCleanup(stack.close)
diff --git a/jsonargparse_tests/test_core.py b/jsonargparse_tests/test_core.py
index 8aeac23..eb154e0 100755
--- a/jsonargparse_tests/test_core.py
+++ b/jsonargparse_tests/test_core.py
@@ -23,13 +23,12 @@ from jsonargparse import (
     ActionJsonSchema,
     ActionParser,
     ActionYesNo,
+    ArgumentError,
     ArgumentParser,
     Namespace,
-    ParserError,
     Path,
     set_config_read_mode,
     strip_meta,
-    usage_and_exit_error_handler,
 )
 from jsonargparse.namespace import meta_keys
 from jsonargparse.optionals import (
@@ -47,12 +46,7 @@ from jsonargparse.typing import (
     PositiveFloat,
     PositiveInt,
 )
-from jsonargparse.util import (
-    CaptureParserException,
-    DebugException,
-    capture_parser,
-    null_logger,
-)
+from jsonargparse.util import CaptureParserException, capture_parser
 from jsonargparse_tests.base import (
     TempDirTestCase,
     is_cpython,
@@ -64,7 +58,7 @@ from jsonargparse_tests.base import (
 
 def example_parser():
     """Creates a simple parser for doing tests."""
-    parser = ArgumentParser(prog='app', default_meta=False, error_handler=None)
+    parser = ArgumentParser(prog='app', default_meta=False, exit_on_error=False)
 
     group_one = parser.add_argument_group('Group 1', name='group1')
     group_one.add_argument('--bools.def_false',
@@ -116,8 +110,8 @@ class ParsersTests(TempDirTestCase):
         self.assertEqual('opt1_arg', parser.parse_args(['--lev1.lev2.opt1', 'opt1_arg']).lev1.lev2.opt1)
         self.assertEqual(9, parser.parse_args(['--nums.val1', '9']).nums.val1)
         self.assertEqual(6.4, parser.parse_args(['--nums.val2', '6.4']).nums.val2)
-        self.assertRaises(ParserError, lambda: parser.parse_args(['--nums.val1', '7.5']))
-        self.assertRaises(ParserError, lambda: parser.parse_args(['--nums.val2', 'eight']))
+        self.assertRaises(ArgumentError, lambda: parser.parse_args(['--nums.val1', '7.5']))
+        self.assertRaises(ArgumentError, lambda: parser.parse_args(['--nums.val2', 'eight']))
         self.assertEqual(9, parser.parse_args(['--nums.val1', '9'])['nums.val1'])
 
 
@@ -133,7 +127,7 @@ class ParsersTests(TempDirTestCase):
         self.assertEqual(False, cfg.bools.def_false)
         self.assertEqual(True,  cfg.bools.def_true)
 
-        self.assertRaises(ParserError, lambda: parser.parse_object({'undefined': True}))
+        self.assertRaises(ArgumentError, lambda: parser.parse_object({'undefined': True}))
 
 
     def test_parse_env(self):
@@ -147,7 +141,7 @@ class ParsersTests(TempDirTestCase):
         parser.add_argument('--cfg', action=ActionConfigFile)
         env = OrderedDict(example_env)
         env['APP_NUMS__VAL1'] = '"""'
-        self.assertRaises(ParserError, lambda: parser.parse_env(env))
+        self.assertRaises(ArgumentError, lambda: parser.parse_env(env))
         env = OrderedDict(example_env)
         env['APP_CFG'] = '{"nums": {"val1": 1}}'
         self.assertEqual(0, parser.parse_env(env).nums.val1)
@@ -186,9 +180,9 @@ class ParsersTests(TempDirTestCase):
 
 
     def test_env_prefix(self):
-        parser = ArgumentParser(env_prefix=True, default_env=True, error_handler=None)
+        parser = ArgumentParser(env_prefix=True, default_env=True, exit_on_error=False)
         parser.add_argument("--test_arg", type=str, required=True, help="Test argument")
-        with self.assertRaises(ParserError):
+        with self.assertRaises(ArgumentError):
             with unittest.mock.patch.dict(os.environ, {'TEST_ARG': 'one'}):
                 parser.parse_args([])
         prefix = os.path.splitext(parser.prog)[0].upper()
@@ -218,12 +212,12 @@ class ParsersTests(TempDirTestCase):
         self.assertFalse(hasattr(cfg2, 'bools'))
         self.assertTrue(hasattr(cfg2, 'nums'))
 
-        self.assertRaises(ParserError, lambda: parser.parse_string('"""'))
+        self.assertRaises(ArgumentError, lambda: parser.parse_string('"""'))
 
 
     def test_parse_string_not_dict(self):
-        parser = ArgumentParser(error_handler=None)
-        with self.assertRaises(ParserError):
+        parser = ArgumentParser(exit_on_error=False)
+        with self.assertRaises(ArgumentError):
             parser.parse_string('not a dict')
 
 
@@ -243,10 +237,10 @@ class ParsersTests(TempDirTestCase):
 
         with open(yaml_file, 'w') as output_file:
             output_file.write(example_yaml+'  val2: eight\n')
-        self.assertRaises(ParserError, lambda: parser.parse_path(yaml_file))
+        self.assertRaises(ArgumentError, lambda: parser.parse_path(yaml_file))
         with open(yaml_file, 'w') as output_file:
             output_file.write(example_yaml+'  val3: key_not_defined\n')
-        self.assertRaises(ParserError, lambda: parser.parse_path(yaml_file))
+        self.assertRaises(ArgumentError, lambda: parser.parse_path(yaml_file))
 
 
     def test_cfg_base(self):
@@ -335,18 +329,18 @@ class ParsersTests(TempDirTestCase):
 class ArgumentFeaturesTests(unittest.TestCase):
 
     def test_positionals(self):
-        parser = ArgumentParser(error_handler=None)
+        parser = ArgumentParser(exit_on_error=False)
         parser.add_argument('pos1')
         parser.add_argument('pos2', nargs='?')
-        self.assertRaises(ParserError, lambda: parser.parse_args([]))
+        self.assertRaises(ArgumentError, lambda: parser.parse_args([]))
         self.assertIsNone(parser.parse_args(['v1']).pos2)
         self.assertEqual('v1', parser.parse_args(['v1']).pos1)
         self.assertEqual('v2', parser.parse_args(['v1', 'v2']).pos2)
 
-        parser = ArgumentParser(error_handler=None)
+        parser = ArgumentParser(exit_on_error=False)
         parser.add_argument('pos1')
         parser.add_argument('pos2', nargs='+')
-        self.assertRaises(ParserError, lambda: parser.parse_args(['v1']).pos2)
+        self.assertRaises(ArgumentError, lambda: parser.parse_args(['v1']).pos2)
         self.assertEqual(['v2', 'v3'], parser.parse_args(['v1', 'v2', 'v3']).pos2)
 
         parser.add_argument('--opt')
@@ -359,7 +353,7 @@ class ArgumentFeaturesTests(unittest.TestCase):
 
 
     def test_required(self):
-        parser = ArgumentParser(env_prefix='APP', error_handler=None)
+        parser = ArgumentParser(env_prefix='APP', exit_on_error=False)
         group = parser.add_argument_group('Group 1')
         group.add_argument('--req1', required=True)
         parser.add_argument('--lev1.req2', required=True)
@@ -372,9 +366,9 @@ class ArgumentFeaturesTests(unittest.TestCase):
         cfg = parser.parse_env({'APP_REQ1': 'val5', 'APP_LEV1__REQ2': 'val6'})
         self.assertEqual('val5', cfg.req1)
         self.assertEqual('val6', cfg.lev1.req2)
-        self.assertRaises(ParserError, lambda: parser.parse_args(['--req1', 'val1']))
-        self.assertRaises(ParserError, lambda: parser.parse_string('{"lev1":{"req2":"val4"}}'))
-        self.assertRaises(ParserError, lambda: parser.parse_env({}))
+        self.assertRaises(ArgumentError, lambda: parser.parse_args(['--req1', 'val1']))
+        self.assertRaises(ArgumentError, lambda: parser.parse_string('{"lev1":{"req2":"val4"}}'))
+        self.assertRaises(ArgumentError, lambda: parser.parse_env({}))
 
         out = StringIO()
         parser.print_help(out)
@@ -388,23 +382,23 @@ class ArgumentFeaturesTests(unittest.TestCase):
 
 
     def test_choices(self):
-        parser = ArgumentParser(error_handler=None)
+        parser = ArgumentParser(exit_on_error=False)
         parser.add_argument('--ch1',
             choices='ABC')
         parser.add_argument('--ch2',
             choices=['v1', 'v2'])
         cfg = parser.parse_args(['--ch1', 'C', '--ch2', 'v1'])
         self.assertEqual(cfg.as_dict(), {'ch1': 'C', 'ch2': 'v1'})
-        self.assertRaises(ParserError, lambda: parser.parse_args(['--ch1', 'D']))
-        self.assertRaises(ParserError, lambda: parser.parse_args(['--ch2', 'v0']))
+        self.assertRaises(ArgumentError, lambda: parser.parse_args(['--ch1', 'D']))
+        self.assertRaises(ArgumentError, lambda: parser.parse_args(['--ch2', 'v0']))
 
 
     def test_nargs(self):
-        parser = ArgumentParser(error_handler=None)
+        parser = ArgumentParser(exit_on_error=False)
         parser.add_argument('--val', nargs='+', type=int)
         self.assertEqual([9],        parser.parse_args(['--val', '9']).val)
         self.assertEqual([3, 6, 2],  parser.parse_args(['--val', '3', '6', '2']).val)
-        self.assertRaises(ParserError, lambda: parser.parse_args(['--val']))
+        self.assertRaises(ArgumentError, lambda: parser.parse_args(['--val']))
         parser = ArgumentParser()
         parser.add_argument('--val', nargs='*', type=float)
         self.assertEqual([5.2, 1.9], parser.parse_args(['--val', '5.2', '1.9']).val)
@@ -424,12 +418,12 @@ class ArgumentFeaturesTests(unittest.TestCase):
 class AdvancedFeaturesTests(unittest.TestCase):
 
     def test_subcommands(self):
-        parser_a = ArgumentParser(error_handler=None)
+        parser_a = ArgumentParser(exit_on_error=False)
         parser_a.add_argument('ap1')
         parser_a.add_argument('--ao1',
             default='ao1_def')
 
-        parser = ArgumentParser(prog='app', error_handler=None)
+        parser = ArgumentParser(prog='app', exit_on_error=False)
         parser.add_argument('--o1',
             default='o1_def')
         subcommands = parser.add_subcommands()
@@ -440,7 +434,7 @@ class AdvancedFeaturesTests(unittest.TestCase):
 
         self.assertRaises(NotImplementedError, lambda: parser.add_subparsers())
         self.assertRaises(NotImplementedError, lambda: subcommands.add_parser(''))
-        self.assertRaises(ParserError, lambda: parser.parse_args(['c']))
+        self.assertRaises(ArgumentError, lambda: parser.parse_args(['c']))
 
         cfg = parser.get_defaults().as_dict()
         self.assertEqual(cfg, {'o1': 'o1_def', 'subcommand': None})
@@ -466,17 +460,17 @@ class AdvancedFeaturesTests(unittest.TestCase):
         self.assertRaises(KeyError, lambda: cfg['a'])
 
         parser.parse_args(['B'])
-        self.assertRaises(ParserError, lambda: parser.parse_args(['A']))
+        self.assertRaises(ArgumentError, lambda: parser.parse_args(['A']))
 
-        self.assertRaises(ParserError, lambda: parser.parse_args())
-        self.assertRaises(ParserError, lambda: parser.parse_args(['a']))
-        self.assertRaises(ParserError, lambda: parser.parse_args(['b', '--unk']))
-        self.assertRaises(ParserError, lambda: parser.parse_args(['c']))
+        self.assertRaises(ArgumentError, lambda: parser.parse_args())
+        self.assertRaises(ArgumentError, lambda: parser.parse_args(['a']))
+        self.assertRaises(ArgumentError, lambda: parser.parse_args(['b', '--unk']))
+        self.assertRaises(ArgumentError, lambda: parser.parse_args(['c']))
 
         cfg = parser.parse_string('{"a": {"ap1": "ap1_cfg"}}').as_dict()
         self.assertEqual(cfg['subcommand'], 'a')
         self.assertEqual(cfg['a'], {'ap1': 'ap1_cfg', 'ao1': 'ao1_def'})
-        self.assertRaises(ParserError, lambda: parser.parse_string('{"a": {"ap1": "ap1_cfg", "unk": "unk_cfg"}}'))
+        self.assertRaises(ArgumentError, lambda: parser.parse_string('{"a": {"ap1": "ap1_cfg", "unk": "unk_cfg"}}'))
 
         with warnings.catch_warnings(record=True) as w:
             cfg = parser.parse_string('{"a": {"ap1": "ap1_cfg"}, "b": {"nums": {"val1": 2}}}')
@@ -522,23 +516,23 @@ class AdvancedFeaturesTests(unittest.TestCase):
 
 
     def test_subsubcommands(self):
-        parser_s1_a = ArgumentParser(error_handler=None)
+        parser_s1_a = ArgumentParser(exit_on_error=False)
         parser_s1_a.add_argument('--os1a',
             default='os1a_def')
 
-        parser_s2_b = ArgumentParser(error_handler=None)
+        parser_s2_b = ArgumentParser(exit_on_error=False)
         parser_s2_b.add_argument('--os2b',
             default='os2b_def')
 
-        parser = ArgumentParser(prog='app', error_handler=None, default_meta=False)
+        parser = ArgumentParser(prog='app', exit_on_error=False, default_meta=False)
         subcommands1 = parser.add_subcommands()
         subcommands1.add_subcommand('a', parser_s1_a)
 
         subcommands2 = parser_s1_a.add_subcommands()
         subcommands2.add_subcommand('b', parser_s2_b)
 
-        self.assertRaises(ParserError, lambda: parser.parse_args([]))
-        self.assertRaises(ParserError, lambda: parser.parse_args(['a']))
+        self.assertRaises(ArgumentError, lambda: parser.parse_args([]))
+        self.assertRaises(ArgumentError, lambda: parser.parse_args(['a']))
 
         cfg = parser.parse_args(['a', 'b']).as_dict()
         self.assertEqual(cfg, {'subcommand': 'a', 'a': {'subcommand': 'b', 'os1a': 'os1a_def', 'b': {'os2b': 'os2b_def'}}})
@@ -561,7 +555,7 @@ class AdvancedFeaturesTests(unittest.TestCase):
 
 
     def test_optional_subcommand(self):
-        parser = ArgumentParser(error_handler=None)
+        parser = ArgumentParser(exit_on_error=False)
         subcommands = parser.add_subcommands(required=False)
         subparser = ArgumentParser()
         subcommands.add_subcommand('foo', subparser)
@@ -583,7 +577,7 @@ class AdvancedFeaturesTests(unittest.TestCase):
         subparser.add_argument('--config', action=ActionConfigFile)
         subparser.add_argument('--o', type=int, default=1)
 
-        parser = ArgumentParser(error_handler=None, default_env=True)
+        parser = ArgumentParser(exit_on_error=False, default_env=True)
         subcommands = parser.add_subcommands()
         subcommands.add_subcommand('a', subparser)
 
@@ -597,7 +591,7 @@ class AdvancedFeaturesTests(unittest.TestCase):
     @responses_activate
     def test_urls(self):
         set_config_read_mode(urls_enabled=True)
-        parser = ArgumentParser(error_handler=None)
+        parser = ArgumentParser(exit_on_error=False)
         parser.add_argument('--cfg',
             action=ActionConfigFile)
         parser.add_argument('--parser',
@@ -758,7 +752,7 @@ class OutputTests(TempDirTestCase):
 
 
     def test_save(self):
-        parser = ArgumentParser(error_handler=None)
+        parser = ArgumentParser(exit_on_error=False)
         parser.add_argument('--parser',
             action=ActionParser(parser=example_parser()))
         if jsonschema_support:
@@ -897,7 +891,7 @@ class OutputTests(TempDirTestCase):
 
 
     def test_print_config(self):
-        parser = ArgumentParser(error_handler=None, description='cli tool')
+        parser = ArgumentParser(exit_on_error=False, description='cli tool')
         parser.add_argument('--cfg', action=ActionConfigFile)
         parser.add_argument('--v0', help=SUPPRESS, default='0')
         parser.add_argument('--v1', help='Option v1.', default=1)
@@ -919,7 +913,7 @@ class OutputTests(TempDirTestCase):
         outval = yaml.safe_load(out.getvalue())
         self.assertEqual(outval, {'g1': {'v2': '2'}, 'g2': {}, 'v1': 1})
 
-        self.assertRaises(ParserError, lambda: parser.parse_args(['--print_config=bad']))
+        self.assertRaises(ArgumentError, lambda: parser.parse_args(['--print_config=bad']))
 
         if docstring_parser_support and ruyaml_support:
             out = StringIO()
@@ -951,7 +945,7 @@ class ConfigFilesTests(TempDirTestCase):
 
     def test_get_default_with_default_config_file(self):
         default_config_file = os.path.realpath(os.path.join(self.tmpdir, 'defaults.yaml'))
-        parser = ArgumentParser(default_config_files=[default_config_file], error_handler=None)
+        parser = ArgumentParser(default_config_files=[default_config_file], exit_on_error=False)
         parser.add_argument('--op1', default='from default')
 
         with open(default_config_file, 'w') as output_file:
@@ -964,7 +958,7 @@ class ConfigFilesTests(TempDirTestCase):
 
         with open(default_config_file, 'w') as output_file:
             output_file.write('op2: v2\n')
-        self.assertRaises(ParserError, lambda: parser.get_default('op1'))
+        self.assertRaises(ArgumentError, lambda: parser.get_default('op1'))
 
         out = StringIO()
         parser.print_help(out)
@@ -978,7 +972,7 @@ class ConfigFilesTests(TempDirTestCase):
 
     def test_get_default_with_multiple_default_config_files(self):
         default_configs_pattern = os.path.realpath(os.path.join(self.tmpdir, 'defaults_*.yaml'))
-        parser = ArgumentParser(default_config_files=[default_configs_pattern], error_handler=None)
+        parser = ArgumentParser(default_config_files=[default_configs_pattern], exit_on_error=False)
         parser.add_argument('--op1', default='from default')
         parser.add_argument('--op2', default='from default')
 
@@ -1025,7 +1019,7 @@ class ConfigFilesTests(TempDirTestCase):
         with open(abs_yaml_file, 'w') as output_file:
             output_file.write('val: yaml\n')
 
-        parser = ArgumentParser(error_handler=None)
+        parser = ArgumentParser(exit_on_error=False)
         parser.add_argument('--cfg', action=ActionConfigFile)
         parser.add_argument('--val')
 
@@ -1038,17 +1032,17 @@ class ConfigFilesTests(TempDirTestCase):
         self.assertEqual(rel_yaml_file, str(cfg.cfg[1]))
         self.assertEqual(None, cfg.cfg[2])
 
-        self.assertRaises(ParserError, lambda: parser.parse_args(['--cfg', '{"k":"v"}']))
+        self.assertRaises(ArgumentError, lambda: parser.parse_args(['--cfg', '{"k":"v"}']))
 
 
     def test_ActionConfigFile_failures(self):
-        parser = ArgumentParser(error_handler=None)
+        parser = ArgumentParser(exit_on_error=False)
         self.assertRaises(ValueError, lambda: parser.add_argument('--cfg', default='config.yaml', action=ActionConfigFile))
         self.assertRaises(ValueError, lambda: parser.add_argument('--nested.cfg', action=ActionConfigFile))
 
         parser.add_argument('--cfg', action=ActionConfigFile)
-        self.assertRaises(ParserError, lambda: parser.parse_args(['--cfg', '"""']))
-        self.assertRaises(ParserError, lambda: parser.parse_args(['--cfg=not-exist']))
+        self.assertRaises(ArgumentError, lambda: parser.parse_args(['--cfg', '"""']))
+        self.assertRaises(ArgumentError, lambda: parser.parse_args(['--cfg=not-exist']))
 
 
 class OtherTests(unittest.TestCase):
@@ -1112,10 +1106,10 @@ class OtherTests(unittest.TestCase):
         parser.check_config(cfg.lev1, branch='lev1')
 
 
-    def test_usage_and_exit_error_handler(self):
+    def test_exit_on_error(self):
         err = StringIO()
         with redirect_stderr(err):
-            parser = ArgumentParser()
+            parser = ArgumentParser(exit_on_error=True)
             parser.add_argument('--val', type=int)
             self.assertEqual(8, parser.parse_args(['--val', '8']).val)
             self.assertRaises(SystemExit, lambda: parser.parse_args(['--val', 'eight']))
@@ -1123,32 +1117,12 @@ class OtherTests(unittest.TestCase):
 
 
     @unittest.mock.patch.dict(os.environ, {'JSONARGPARSE_DEBUG': ''})
-    def test_debug_usage_and_exit_error_handler(self):
-        parser = ArgumentParser(logger=null_logger)
+    def test_debug_environment_variable(self):
+        parser = ArgumentParser(logger={'level': 'DEBUG'})
         parser.add_argument('--int', type=int)
-        err = StringIO()
-        with redirect_stderr(err), self.assertRaises(DebugException):
+        with self.assertLogs(logger=parser.logger, level='DEBUG') as log, self.assertRaises(ArgumentError):
             parser.parse_args(['--int=invalid'])
-
-
-    def test_error_handler_property(self):
-        parser = ArgumentParser()
-        self.assertEqual(parser.error_handler, usage_and_exit_error_handler)
-
-        def custom_error_handler(self, message):
-            print('custom_error_handler')
-            self.exit(2)
-
-        parser.error_handler = custom_error_handler
-        self.assertEqual(parser.error_handler, custom_error_handler)
-
-        out = StringIO()
-        with redirect_stdout(out), self.assertRaises(SystemExit):
-            parser.parse_args(['--invalid'])
-        self.assertEqual(out.getvalue(), 'custom_error_handler\n')
-
-        with self.assertRaises(ValueError):
-            parser.error_handler = 'invalid'
+        self.assertTrue(any("Debug enabled" in o for o in log.output))
 
 
     def test_version_print(self):
@@ -1188,8 +1162,8 @@ class OtherTests(unittest.TestCase):
             self.assertRaises(NotImplementedError, lambda: parser.parse_known_args([]))
 
     def test_parse_args_invalid_args(self):
-        parser = ArgumentParser(error_handler=None)
-        self.assertRaises(ParserError, lambda: parser.parse_args([{}]))
+        parser = ArgumentParser(exit_on_error=False)
+        self.assertRaises(ArgumentError, lambda: parser.parse_args([{}]))
 
 
     @unittest.skipIf(sys.version_info[:2] == (3, 6), 'loggers not pickleable in python 3.6')
diff --git a/jsonargparse_tests/test_deprecated.py b/jsonargparse_tests/test_deprecated.py
index 8c13931..9ca82db 100755
--- a/jsonargparse_tests/test_deprecated.py
+++ b/jsonargparse_tests/test_deprecated.py
@@ -4,6 +4,7 @@ import os
 import pathlib
 import unittest
 from calendar import Calendar
+from contextlib import redirect_stdout
 from enum import Enum
 from io import StringIO
 from warnings import catch_warnings
@@ -11,8 +12,8 @@ from warnings import catch_warnings
 from jsonargparse import (
     CLI,
     ActionConfigFile,
+    ArgumentError,
     ArgumentParser,
-    ParserError,
     Path,
     get_config_read_mode,
     set_url_support,
@@ -22,13 +23,15 @@ from jsonargparse.deprecated import (
     ActionOperators,
     ActionPath,
     ActionPathList,
+    ParserError,
     deprecation_warning,
     import_docstring_parse,
     shown_deprecation_warnings,
+    usage_and_exit_error_handler,
 )
 from jsonargparse.optionals import docstring_parser_support, url_support
-from jsonargparse.util import LoggerProperty
-from jsonargparse_tests.base import TempDirTestCase
+from jsonargparse.util import LoggerProperty, argument_error
+from jsonargparse_tests.base import TempDirTestCase, suppress_stderr
 
 
 class DeprecatedTests(unittest.TestCase):
@@ -54,7 +57,7 @@ class DeprecatedTests(unittest.TestCase):
             B = 2
             C = 3
 
-        parser = ArgumentParser(error_handler=None)
+        parser = ArgumentParser(exit_on_error=False)
         with catch_warnings(record=True) as w:
             action = ActionEnum(enum=MyEnum)
             self.assertIn('ActionEnum was deprecated', str(w[-1].message))
@@ -66,7 +69,7 @@ class DeprecatedTests(unittest.TestCase):
         for val in ['A', 'B', 'C']:
             self.assertEqual(MyEnum[val], parser.parse_args(['--enum='+val]).enum)
         for val in ['X', 'b', 2]:
-            self.assertRaises(ParserError, lambda: parser.parse_args(['--enum='+str(val)]))
+            self.assertRaises(ArgumentError, lambda: parser.parse_args(['--enum='+str(val)]))
 
         cfg = parser.parse_args(['--enum=C'], with_meta=False)
         self.assertEqual('enum: C\n', parser.dump(cfg))
@@ -90,7 +93,7 @@ class DeprecatedTests(unittest.TestCase):
 
 
     def test_ActionOperators(self):
-        parser = ArgumentParser(prog='app', error_handler=None)
+        parser = ArgumentParser(prog='app', exit_on_error=False)
         with catch_warnings(record=True) as w:
             parser.add_argument('--le0',
                 action=ActionOperators(expr=('<', 0)))
@@ -105,24 +108,24 @@ class DeprecatedTests(unittest.TestCase):
 
         self.assertEqual(1.5, parser.parse_args(['--gt1.a.le4', '1.5']).gt1.a.le4)
         self.assertEqual(4.0, parser.parse_args(['--gt1.a.le4', '4.0']).gt1.a.le4)
-        self.assertRaises(ParserError, lambda: parser.parse_args(['--gt1.a.le4', '1.0']))
-        self.assertRaises(ParserError, lambda: parser.parse_args(['--gt1.a.le4', '5.5']))
+        self.assertRaises(ArgumentError, lambda: parser.parse_args(['--gt1.a.le4', '1.0']))
+        self.assertRaises(ArgumentError, lambda: parser.parse_args(['--gt1.a.le4', '5.5']))
 
         self.assertEqual(1.5, parser.parse_string('gt1:\n  a:\n    le4: 1.5').gt1.a.le4)
         self.assertEqual(4.0, parser.parse_string('gt1:\n  a:\n    le4: 4.0').gt1.a.le4)
-        self.assertRaises(ParserError, lambda: parser.parse_string('gt1:\n  a:\n    le4: 1.0'))
-        self.assertRaises(ParserError, lambda: parser.parse_string('gt1:\n  a:\n    le4: 5.5'))
+        self.assertRaises(ArgumentError, lambda: parser.parse_string('gt1:\n  a:\n    le4: 1.0'))
+        self.assertRaises(ArgumentError, lambda: parser.parse_string('gt1:\n  a:\n    le4: 5.5'))
 
         self.assertEqual(1.5, parser.parse_env({'APP_GT1__A__LE4': '1.5'}).gt1.a.le4)
         self.assertEqual(4.0, parser.parse_env({'APP_GT1__A__LE4': '4.0'}).gt1.a.le4)
-        self.assertRaises(ParserError, lambda: parser.parse_env({'APP_GT1__A__LE4': '1.0'}))
-        self.assertRaises(ParserError, lambda: parser.parse_env({'APP_GT1__A__LE4': '5.5'}))
+        self.assertRaises(ArgumentError, lambda: parser.parse_env({'APP_GT1__A__LE4': '1.0'}))
+        self.assertRaises(ArgumentError, lambda: parser.parse_env({'APP_GT1__A__LE4': '5.5'}))
 
         self.assertEqual(2, parser.parse_args(['--lt5.o.ge10.o.eq7', '2']).lt5.o.ge10.o.eq7)
         self.assertEqual(7, parser.parse_args(['--lt5.o.ge10.o.eq7', '7']).lt5.o.ge10.o.eq7)
         self.assertEqual(10, parser.parse_args(['--lt5.o.ge10.o.eq7', '10']).lt5.o.ge10.o.eq7)
-        self.assertRaises(ParserError, lambda: parser.parse_args(['--lt5.o.ge10.o.eq7', '5']))
-        self.assertRaises(ParserError, lambda: parser.parse_args(['--lt5.o.ge10.o.eq7', '8']))
+        self.assertRaises(ArgumentError, lambda: parser.parse_args(['--lt5.o.ge10.o.eq7', '5']))
+        self.assertRaises(ArgumentError, lambda: parser.parse_args(['--lt5.o.ge10.o.eq7', '8']))
 
         self.assertEqual([0, 1, 2], parser.parse_args(['--ge0', '0', '1', '2']).ge0)
 
@@ -159,7 +162,7 @@ class DeprecatedTests(unittest.TestCase):
 
 
     def test_instantiate_subclasses(self):
-        parser = ArgumentParser(error_handler=None)
+        parser = ArgumentParser(exit_on_error=False)
         parser.add_argument('--cal', type=Calendar)
         cfg = parser.parse_object({'cal':{'class_path': 'calendar.Calendar'}})
         with catch_warnings(record=True) as w:
@@ -210,6 +213,39 @@ class DeprecatedTests(unittest.TestCase):
             self.assertIn('Only use the public API', str(w[-1].message))
 
 
+    def test_error_handler_parameter(self):
+        with catch_warnings(record=True) as w:
+            parser = ArgumentParser(error_handler=usage_and_exit_error_handler)
+            self.assertIn('error_handler was deprecated in v4.20.0', str(w[-1].message))
+        self.assertEqual(parser.error_handler, usage_and_exit_error_handler)
+        with suppress_stderr(), self.assertRaises(SystemExit):
+            parser.parse_args(['--invalid'])
+
+
+    def test_error_handler_property(self):
+        def custom_error_handler(self, message):
+            print('custom_error_handler')
+            self.exit(2)
+
+        parser = ArgumentParser()
+        with catch_warnings(record=True) as w:
+            parser.error_handler = custom_error_handler
+            self.assertIn('error_handler was deprecated in v4.20.0', str(w[-1].message))
+        self.assertEqual(parser.error_handler, custom_error_handler)
+
+        out = StringIO()
+        with redirect_stdout(out), self.assertRaises(SystemExit):
+            parser.parse_args(['--invalid'])
+        self.assertEqual(out.getvalue(), 'custom_error_handler\n')
+
+        with self.assertRaises(ValueError):
+            parser.error_handler = 'invalid'
+
+
+    def test_ParserError(self):
+        self.assertIsInstance(argument_error(''), ParserError)
+
+
 class DeprecatedTempDirTests(TempDirTestCase):
 
     def test_parse_as_dict(self):
@@ -237,7 +273,7 @@ class DeprecatedTempDirTests(TempDirTestCase):
         with open(abs_yaml_file, 'w') as output_file:
             output_file.write('file: '+rel_yaml_file+'\ndir: '+self.tmpdir+'\n')
 
-        parser = ArgumentParser(error_handler=None)
+        parser = ArgumentParser(exit_on_error=False)
         parser.add_argument('--cfg', action=ActionConfigFile)
         with catch_warnings(record=True) as w:
             parser.add_argument('--file', action=ActionPath(mode='fr'))
@@ -251,19 +287,19 @@ class DeprecatedTempDirTests(TempDirTestCase):
         self.assertEqual(abs_yaml_file, os.path.realpath(cfg.cfg[0](absolute=True)))
         self.assertEqual(rel_yaml_file, cfg.file(absolute=False))
         self.assertEqual(abs_yaml_file, os.path.realpath(cfg.file(absolute=True)))
-        self.assertRaises(ParserError, lambda: parser.parse_args(['--cfg', abs_yaml_file+'~']))
+        self.assertRaises(ArgumentError, lambda: parser.parse_args(['--cfg', abs_yaml_file+'~']))
 
         cfg = parser.parse_args(['--cfg', 'file: '+abs_yaml_file+'\ndir: '+self.tmpdir+'\n'])
         self.assertEqual(self.tmpdir, os.path.realpath(cfg.dir(absolute=True)))
         self.assertEqual(None, cfg.cfg[0])
         self.assertEqual(abs_yaml_file, os.path.realpath(cfg.file(absolute=True)))
-        self.assertRaises(ParserError, lambda: parser.parse_args(['--cfg', '{"k":"v"}']))
+        self.assertRaises(ArgumentError, lambda: parser.parse_args(['--cfg', '{"k":"v"}']))
 
         cfg = parser.parse_args(['--file', abs_yaml_file, '--dir', self.tmpdir])
         self.assertEqual(self.tmpdir, os.path.realpath(cfg.dir(absolute=True)))
         self.assertEqual(abs_yaml_file, os.path.realpath(cfg.file(absolute=True)))
-        self.assertRaises(ParserError, lambda: parser.parse_args(['--dir', abs_yaml_file]))
-        self.assertRaises(ParserError, lambda: parser.parse_args(['--file', self.tmpdir]))
+        self.assertRaises(ArgumentError, lambda: parser.parse_args(['--dir', abs_yaml_file]))
+        self.assertRaises(ArgumentError, lambda: parser.parse_args(['--file', self.tmpdir]))
 
         cfg = parser.parse_args(['--files', abs_yaml_file, abs_yaml_file])
         self.assertTrue(isinstance(cfg.files, list))
@@ -276,7 +312,7 @@ class DeprecatedTempDirTests(TempDirTestCase):
 
 
     def test_ActionPath_skip_check(self):
-        parser = ArgumentParser(error_handler=None)
+        parser = ArgumentParser(exit_on_error=False)
         with catch_warnings(record=True) as w:
             parser.add_argument('--file', action=ActionPath(mode='fr', skip_check=True))
             self.assertIn('skip_check parameter of Path was deprecated', str(w[-1].message))
@@ -342,7 +378,7 @@ class DeprecatedTempDirTests(TempDirTestCase):
         with open(list_file4, 'w') as output_file:
             output_file.write('file1\nfile2\nfile6\n')
 
-        parser = ArgumentParser(prog='app', error_handler=None)
+        parser = ArgumentParser(prog='app', exit_on_error=False)
         with catch_warnings(record=True) as w:
             parser.add_argument('--list',
                 nargs='+',
@@ -368,9 +404,9 @@ class DeprecatedTempDirTests(TempDirTestCase):
         self.assertEqual(['file1', 'file2', 'file3', 'file4'], [str(x) for x in cfg.list_cwd])
         os.chdir(cwd)
 
-        self.assertRaises(ParserError, lambda: parser.parse_args(['--list']))
-        self.assertRaises(ParserError, lambda: parser.parse_args(['--list', list_file4]))
-        self.assertRaises(ParserError, lambda: parser.parse_args(['--list', 'no-such-file']))
+        self.assertRaises(ArgumentError, lambda: parser.parse_args(['--list']))
+        self.assertRaises(ArgumentError, lambda: parser.parse_args(['--list', list_file4]))
+        self.assertRaises(ArgumentError, lambda: parser.parse_args(['--list', 'no-such-file']))
 
         self.assertRaises(ValueError, lambda: parser.add_argument('--op1', action=ActionPathList))
         self.assertRaises(ValueError, lambda: parser.add_argument('--op2', action=ActionPathList(mode='fr'), nargs='*'))
diff --git a/jsonargparse_tests/test_jsonnet.py b/jsonargparse_tests/test_jsonnet.py
index 086dce0..339e4fa 100755
--- a/jsonargparse_tests/test_jsonnet.py
+++ b/jsonargparse_tests/test_jsonnet.py
@@ -14,8 +14,8 @@ from jsonargparse import (
     ActionJsonnet,
     ActionJsonnetExtVars,
     ActionJsonSchema,
+    ArgumentError,
     ArgumentParser,
-    ParserError,
     strip_meta,
 )
 from jsonargparse.optionals import jsonnet_support
@@ -71,7 +71,7 @@ example_schema = {
 class JsonnetTests(TempDirTestCase):
 
     def test_parser_mode_jsonnet(self):
-        parser = ArgumentParser(parser_mode='jsonnet', error_handler=None)
+        parser = ArgumentParser(parser_mode='jsonnet', exit_on_error=False)
         parser.add_argument('--cfg',
             action=ActionConfigFile)
         parser.add_argument('--param',
@@ -89,7 +89,7 @@ class JsonnetTests(TempDirTestCase):
         self.assertEqual('#8', cfg.records[-2]['ref'])
         self.assertEqual(15.5, cfg.records[-2]['val'])
 
-        self.assertRaises(ParserError, lambda: parser.parse_args(['--cfg', '{}}']))
+        self.assertRaises(ArgumentError, lambda: parser.parse_args(['--cfg', '{}}']))
 
 
     def test_parser_mode_jsonnet_import_issue_122(self):
@@ -123,7 +123,7 @@ class JsonnetTests(TempDirTestCase):
             def __init__(self, name: str = 'Lucky', prize: int = 100):
                 pass
 
-        parser = ArgumentParser(parser_mode='jsonnet', error_handler=None)
+        parser = ArgumentParser(parser_mode='jsonnet', exit_on_error=False)
         parser.add_class_arguments(Class, 'group', sub_configs=True)
 
         cfg = parser.parse_args([f'--group={config_path}'])
@@ -132,7 +132,7 @@ class JsonnetTests(TempDirTestCase):
 
 
     def test_ActionJsonnet(self):
-        parser = ArgumentParser(default_meta=False, error_handler=None)
+        parser = ArgumentParser(default_meta=False, exit_on_error=False)
         parser.add_argument('--input.ext_vars',
             action=ActionJsonnetExtVars())
         parser.add_argument('--input.jsonnet',
@@ -147,15 +147,15 @@ class JsonnetTests(TempDirTestCase):
         cfg1 = parser.parse_args(['--input.jsonnet', example_1_jsonnet])
         self.assertEqual(cfg1.input.jsonnet['records'], cfg2.input.jsonnet['records'])
 
-        self.assertRaises(ParserError, lambda: parser.parse_args(['--input.ext_vars', '{"param": "a"}', '--input.jsonnet', example_2_jsonnet]))
-        self.assertRaises(ParserError, lambda: parser.parse_args(['--input.jsonnet', example_2_jsonnet]))
+        self.assertRaises(ArgumentError, lambda: parser.parse_args(['--input.ext_vars', '{"param": "a"}', '--input.jsonnet', example_2_jsonnet]))
+        self.assertRaises(ArgumentError, lambda: parser.parse_args(['--input.jsonnet', example_2_jsonnet]))
 
         self.assertRaises(ValueError, lambda: ActionJsonnet(ext_vars=2))
         self.assertRaises(ValueError, lambda: ActionJsonnet(schema='.'+json.dumps(example_schema)))
 
 
     def test_ActionJsonnet_save(self):
-        parser = ArgumentParser(error_handler=None)
+        parser = ArgumentParser(exit_on_error=False)
         parser.add_argument('--ext_vars',
             action=ActionJsonnetExtVars())
         parser.add_argument('--jsonnet',
@@ -210,7 +210,7 @@ class JsonnetTests(TempDirTestCase):
 
 
     def test_ActionJsonnet_parse(self):
-        parser = ArgumentParser(error_handler=None)
+        parser = ArgumentParser(exit_on_error=False)
         parser.add_argument('--ext_vars',
             action=ActionJsonnetExtVars())
 
diff --git a/jsonargparse_tests/test_jsonschema.py b/jsonargparse_tests/test_jsonschema.py
index 9bb345c..11367e7 100755
--- a/jsonargparse_tests/test_jsonschema.py
+++ b/jsonargparse_tests/test_jsonschema.py
@@ -6,7 +6,12 @@ import re
 import unittest
 from io import StringIO
 
-from jsonargparse import ActionConfigFile, ActionJsonSchema, ArgumentParser, ParserError
+from jsonargparse import (
+    ActionConfigFile,
+    ActionJsonSchema,
+    ArgumentError,
+    ArgumentParser,
+)
 from jsonargparse.optionals import jsonschema_support
 from jsonargparse_tests.base import TempDirTestCase, is_posix
 
@@ -50,7 +55,7 @@ schema3 = {
 class JsonSchemaTests(TempDirTestCase):
 
     def test_ActionJsonSchema(self):
-        parser = ArgumentParser(prog='app', default_meta=False, error_handler=None)
+        parser = ArgumentParser(prog='app', default_meta=False, exit_on_error=False)
         parser.add_argument('--op1',
             action=ActionJsonSchema(schema=schema1))
         parser.add_argument('--op2',
@@ -64,14 +69,14 @@ class JsonSchemaTests(TempDirTestCase):
         op2_val = {'k1': 'one', 'k2': 2, 'k3': 3.3}
 
         self.assertEqual(op1_val, parser.parse_args(['--op1', str(op1_val)]).op1)
-        self.assertRaises(ParserError, lambda: parser.parse_args(['--op1', '[1, "two"]']))
-        self.assertRaises(ParserError, lambda: parser.parse_args(['--op1', '[1.5, 2]']))
+        self.assertRaises(ArgumentError, lambda: parser.parse_args(['--op1', '[1, "two"]']))
+        self.assertRaises(ArgumentError, lambda: parser.parse_args(['--op1', '[1.5, 2]']))
 
         self.assertEqual(op2_val, parser.parse_args(['--op2', str(op2_val)]).op2)
         self.assertEqual(17, parser.parse_args(['--op2', '{"k2": 2}']).op2['k3'])
-        self.assertRaises(ParserError, lambda: parser.parse_args(['--op2', '{"k1": 1}']))
-        self.assertRaises(ParserError, lambda: parser.parse_args(['--op2', '{"k2": "2"}']))
-        self.assertRaises(ParserError, lambda: parser.parse_args(['--op2', '{"k4": 4}']))
+        self.assertRaises(ArgumentError, lambda: parser.parse_args(['--op2', '{"k1": 1}']))
+        self.assertRaises(ArgumentError, lambda: parser.parse_args(['--op2', '{"k2": "2"}']))
+        self.assertRaises(ArgumentError, lambda: parser.parse_args(['--op2', '{"k4": 4}']))
 
         op1_file = os.path.join(self.tmpdir, 'op1.json')
         op2_file = os.path.join(self.tmpdir, 'op2.json')
@@ -101,7 +106,7 @@ class JsonSchemaTests(TempDirTestCase):
 
         if is_posix:
             os.chmod(op1_file, 0)
-            self.assertRaises(ParserError, lambda: parser.parse_path(cfg1_file))
+            self.assertRaises(ArgumentError, lambda: parser.parse_path(cfg1_file))
 
 
     def test_ActionJsonSchema_failures(self):
diff --git a/jsonargparse_tests/test_link_arguments.py b/jsonargparse_tests/test_link_arguments.py
index eae5f32..f26b443 100755
--- a/jsonargparse_tests/test_link_arguments.py
+++ b/jsonargparse_tests/test_link_arguments.py
@@ -11,9 +11,9 @@ import yaml
 
 from jsonargparse import (
     ActionConfigFile,
+    ArgumentError,
     ArgumentParser,
     Namespace,
-    ParserError,
     lazy_instance,
 )
 from jsonargparse.optionals import docstring_parser_support
@@ -45,7 +45,7 @@ class LinkArgumentsTests(unittest.TestCase):
 
 
     def test_link_arguments_on_parse_compute_fn_single_arguments(self):
-        parser = ArgumentParser(error_handler=None)
+        parser = ArgumentParser(exit_on_error=False)
         parser.add_argument('--a.v1', default=2)
         parser.add_argument('--a.v2', type=int, default=3)
         parser.add_argument('--b.v2', type=int, default=4)
@@ -55,14 +55,14 @@ class LinkArgumentsTests(unittest.TestCase):
 
         cfg = parser.parse_args(['--a.v2=-5'])
         self.assertEqual(cfg.b.v2, cfg.a.v1*cfg.a.v2)
-        self.assertRaises(ParserError, lambda: parser.parse_args(['--a.v1=x']))
+        self.assertRaises(ArgumentError, lambda: parser.parse_args(['--a.v1=x']))
 
         dump = yaml.safe_load(parser.dump(cfg))
         self.assertEqual(dump, {'a': {'v1': 2, 'v2': -5}})
 
 
     def test_link_arguments_on_parse_compute_fn_subclass_spec(self):
-        parser = ArgumentParser(error_handler=None)
+        parser = ArgumentParser(exit_on_error=False)
         parser.add_argument('--cfg', action=ActionConfigFile)
         parser.add_argument('--cal1', type=Calendar, default=lazy_instance(TextCalendar))
         parser.add_argument('--cal2', type=Calendar, default=lazy_instance(Calendar))
@@ -83,11 +83,11 @@ class LinkArgumentsTests(unittest.TestCase):
             self.assertEqual(cfg.cal2.init_args.firstweekday, 3)
 
         with self.subTest('invalid arg'):
-            with self.assertRaises(ParserError) as cm:
+            with self.assertRaises(ArgumentError) as cm:
                 parser.parse_args(['--cal1.class_path.init_args.firstweekday=2'])
             self.assertIn('Parser key "cal1"', str(cm.exception))
             parser.set_defaults(cal1=None)
-            with self.assertRaises(ParserError) as cm:
+            with self.assertRaises(ArgumentError) as cm:
                 parser.parse_args(['--cal1.firstweekday=-'])
             self.assertIn('Parser key "cal1"', str(cm.exception))
 
@@ -104,7 +104,7 @@ class LinkArgumentsTests(unittest.TestCase):
                     v1: b v1 help
                 """
 
-        parser = ArgumentParser(error_handler=None)
+        parser = ArgumentParser(exit_on_error=False)
         parser.add_class_arguments(ClassA, 'a')
         parser.add_class_arguments(ClassB, 'b')
         parser.link_arguments('a.v2', 'b.v1')
@@ -133,7 +133,7 @@ class LinkArgumentsTests(unittest.TestCase):
                 self.assertIn('b v1 help', help_str.getvalue())
 
         with self.subTest('failure cases'):
-            self.assertRaises(ParserError, lambda: parser.parse_args(['--b.v1=5']))
+            self.assertRaises(ArgumentError, lambda: parser.parse_args(['--b.v1=5']))
             self.assertRaises(ValueError, lambda: parser.link_arguments('a.v2', 'b.v1'))
             self.assertRaises(ValueError, lambda: parser.link_arguments('x', 'b.v2'))
             self.assertRaises(ValueError, lambda: parser.link_arguments('a.v1', 'x'))
@@ -150,7 +150,7 @@ class LinkArgumentsTests(unittest.TestCase):
             ):
                 pass
 
-        parser = ArgumentParser(error_handler=None)
+        parser = ArgumentParser(exit_on_error=False)
         parser.add_subclass_arguments(ClassA, 'a')
         parser.add_subclass_arguments(Calendar, 'c')
 
@@ -179,7 +179,7 @@ class LinkArgumentsTests(unittest.TestCase):
             self.assertRaises(ValueError, lambda: parser.link_arguments('a.init_args.v1', 'c.init_args'))
 
             a_value['init_args'] = {'v1': 'a', 'v2': 'b'}
-            with self.assertRaises(ParserError):
+            with self.assertRaises(ArgumentError):
                 parser.parse_args(['--a='+json.dumps(a_value), '--c=calendar.Calendar'])
 
 
@@ -216,7 +216,7 @@ class LinkArgumentsTests(unittest.TestCase):
             ):
                 self.c = c
 
-        parser = ArgumentParser(error_handler=None)
+        parser = ArgumentParser(exit_on_error=False)
         parser.add_subclass_arguments(ClassA, 'a')
         parser.add_subclass_arguments(Calendar, 'c', instantiate=False)
         parser.link_arguments('c', 'a.init_args.c')
@@ -257,7 +257,7 @@ class LinkArgumentsTests(unittest.TestCase):
         def return_dict(value: dict):
             return value
 
-        parser = ArgumentParser(error_handler=None)
+        parser = ArgumentParser(exit_on_error=False)
         parser.add_subclass_arguments(ClassA, 'a')
         parser.add_subclass_arguments(Calendar, 'c')
         parser.link_arguments('c', 'a.init_args.a1', compute_fn=return_dict)
@@ -320,7 +320,7 @@ class LinkArgumentsTests(unittest.TestCase):
                 self.c2 = c2
 
         def make_parser_1():
-            parser = ArgumentParser(error_handler=None)
+            parser = ArgumentParser(exit_on_error=False)
             parser.add_class_arguments(ClassA, 'a')
             parser.add_class_arguments(ClassB, 'b')
             parser.add_class_arguments(ClassC, 'c')
@@ -333,7 +333,7 @@ class LinkArgumentsTests(unittest.TestCase):
             return obj_b.b2
 
         def make_parser_2():
-            parser = ArgumentParser(error_handler=None)
+            parser = ArgumentParser(exit_on_error=False)
             parser.add_subclass_arguments(ClassA, 'a')
             parser.add_subclass_arguments(ClassB, 'b')
             parser.link_arguments('b', 'a.init_args.a1', get_b2, apply_on='instantiate')
@@ -489,7 +489,7 @@ class LinkArgumentsTests(unittest.TestCase):
             def __init__(self, b1: int = 2):
                 self.b1 = b1
 
-        parser = ArgumentParser(error_handler=None, logger={'level': 'DEBUG'})
+        parser = ArgumentParser(exit_on_error=False, logger={'level': 'DEBUG'})
         with mock_module(ClassA, ClassB) as module, self.assertLogs(logger=parser.logger, level='DEBUG') as log:
             parser.add_subclass_arguments(ClassA, 'a', default=lazy_instance(ClassA))
             parser.add_subclass_arguments(ClassB, 'b', default=lazy_instance(ClassB))
@@ -500,7 +500,7 @@ class LinkArgumentsTests(unittest.TestCase):
             self.assertTrue(any('a.init_args.a2 --> b.init_args.b1 ignored since source' in x for x in log.output))
             self.assertTrue(any('a.init_args.a1 --> b.init_args.b2 ignored since target' in x for x in log.output))
 
-        parser = ArgumentParser(error_handler=None, logger={'level': 'DEBUG'})
+        parser = ArgumentParser(exit_on_error=False, logger={'level': 'DEBUG'})
         with mock_module(ClassA, ClassB) as module, self.assertLogs(logger=parser.logger, level='DEBUG') as log:
             parser.add_subclass_arguments(ClassA, 'a', default=lazy_instance(ClassA))
             parser.add_subclass_arguments(ClassB, 'b', default=lazy_instance(ClassB))
diff --git a/jsonargparse_tests/test_loaders_dumpers.py b/jsonargparse_tests/test_loaders_dumpers.py
index 7dd9cd5..a667090 100755
--- a/jsonargparse_tests/test_loaders_dumpers.py
+++ b/jsonargparse_tests/test_loaders_dumpers.py
@@ -16,7 +16,7 @@ from jsonargparse.loaders_dumpers import load_value, loaders, yaml_dump
 class LoadersTests(unittest.TestCase):
 
     def test_set_dumper_custom_yaml(self):
-        parser = ArgumentParser(error_handler=None)
+        parser = ArgumentParser(exit_on_error=False)
         parser.add_argument('--list', type=List[int])
 
         def custom_yaml_dump(data) -> str:
@@ -30,7 +30,7 @@ class LoadersTests(unittest.TestCase):
 
 
     def test_disable_implicit_mapping_values(self):
-        parser = ArgumentParser(error_handler=None)
+        parser = ArgumentParser(exit_on_error=False)
         parser.add_argument('--val', type=str)
         self.assertEqual('{one}', parser.parse_args(['--val={one}']).val)
         self.assertEqual('{one,two,three}', parser.parse_args(['--val={one,two,three}']).val)
@@ -38,7 +38,7 @@ class LoadersTests(unittest.TestCase):
 
     @unittest.skipIf(not find_spec('omegaconf'), 'omegaconf package is required')
     def test_parser_mode_omegaconf(self):
-        parser = ArgumentParser(error_handler=None, parser_mode='omegaconf')
+        parser = ArgumentParser(exit_on_error=False, parser_mode='omegaconf')
         parser.add_argument('--server.host', type=str)
         parser.add_argument('--server.port', type=int)
         parser.add_argument('--client.url', type=str)
@@ -66,7 +66,7 @@ class LoadersTests(unittest.TestCase):
         subparser.add_argument('--source', type=str)
         subparser.add_argument('--target', type=str)
 
-        parser = ArgumentParser(error_handler=None, parser_mode='omegaconf')
+        parser = ArgumentParser(exit_on_error=False, parser_mode='omegaconf')
         subcommands = parser.add_subcommands()
         subcommands.add_subcommand('sub', subparser)
 
diff --git a/jsonargparse_tests/test_signatures.py b/jsonargparse_tests/test_signatures.py
index e9d9100..6a4d3cb 100755
--- a/jsonargparse_tests/test_signatures.py
+++ b/jsonargparse_tests/test_signatures.py
@@ -17,9 +17,9 @@ import yaml
 
 from jsonargparse import (
     ActionConfigFile,
+    ArgumentError,
     ArgumentParser,
     Namespace,
-    ParserError,
     class_from_function,
     compose_dataclasses,
     lazy_instance,
@@ -92,7 +92,7 @@ class SignaturesTests(unittest.TestCase):
                 super().__init__(None, c3_a4, **kwargs)
 
         ## Test without nesting ##
-        parser = ArgumentParser(error_handler=None)
+        parser = ArgumentParser(exit_on_error=False)
         parser.add_class_arguments(Class3)
 
         self.assertRaises(ValueError, lambda: parser.add_class_arguments('Class3'))
@@ -124,7 +124,7 @@ class SignaturesTests(unittest.TestCase):
         self.assertEqual(('3', 3, 3.0), parser.parse_args(['--c3_a0=0', '--c3_a7=["3", 3, 3.0]']).c3_a7)
         self.assertEqual('a', Class3(**cfg.as_dict())())
 
-        self.assertRaises(ParserError, lambda: parser.parse_args([]))  # c3_a0 is required
+        self.assertRaises(ArgumentError, lambda: parser.parse_args([]))  # c3_a0 is required
 
         if docstring_parser_support:
             self.assertEqual('Class3 short description', parser.groups['Class3'].title)
@@ -159,7 +159,7 @@ class SignaturesTests(unittest.TestCase):
         class NoArgs:
             pass
 
-        parser = ArgumentParser(error_handler=None)
+        parser = ArgumentParser(exit_on_error=False)
         parser.add_argument('--cfg', action=ActionConfigFile)
         parser.add_class_arguments(NoArgs, 'noargs')
 
@@ -174,7 +174,7 @@ class SignaturesTests(unittest.TestCase):
 
         with mock_module(NoArgs) as module:
             config = {'noargs': {'class_path': f'{module}.NoArgs'}}
-            with self.assertRaises(ParserError):
+            with self.assertRaises(ArgumentError):
                 parser.parse_args([f'--cfg={config}'])
 
 
@@ -191,7 +191,7 @@ class SignaturesTests(unittest.TestCase):
         def func(a1: Union[int, Dict[int, int]] = 1):
             pass
 
-        parser = ArgumentParser(error_handler=None)
+        parser = ArgumentParser(exit_on_error=False)
         parser.add_function_arguments(func)
         parser.get_defaults()
         self.assertEqual({2: 7, 4: 9}, parser.parse_args(['--a1={"2": 7, "4": 9}']).a1)
@@ -206,7 +206,7 @@ class SignaturesTests(unittest.TestCase):
                 obj.a2 = a2  # type: ignore
                 return obj
 
-        parser = ArgumentParser(error_handler=None)
+        parser = ArgumentParser(exit_on_error=False)
         parser.add_class_arguments(ClassA, 'a')
 
         cfg = parser.parse_args(['--a.a1=4'])
@@ -218,7 +218,7 @@ class SignaturesTests(unittest.TestCase):
             def __init__(self, n: int, m: float):
                 pass
 
-        parser = ArgumentParser(error_handler=None)
+        parser = ArgumentParser(exit_on_error=False)
         parser.add_class_arguments(Model, 'model')
         parser.add_argument('--config', action=ActionConfigFile)
 
@@ -228,7 +228,7 @@ class SignaturesTests(unittest.TestCase):
             ['--model.m=0.1'],
             ['--model.n=x', '--model.m=0.1'],
         ]:
-            with self.assertRaises(ParserError):
+            with self.assertRaises(ArgumentError):
                 parser.parse_args(args)
 
         out = StringIO()
@@ -388,7 +388,7 @@ class SignaturesTests(unittest.TestCase):
 
 
     def test_add_subclass_arguments(self):
-        parser = ArgumentParser(error_handler=None)
+        parser = ArgumentParser(exit_on_error=False)
         parser.add_subclass_arguments(Calendar, 'cal')
 
         cal = {'class_path': 'calendar.Calendar', 'init_args': {'firstweekday': 1}}
@@ -407,10 +407,10 @@ class SignaturesTests(unittest.TestCase):
         cfg = parser.parse_args(['--cal.class_path=calendar.Calendar', '--cal.init_args.firstweekday=4', '--cal.class_path=calendar.Calendar'])
         self.assertEqual(cfg['cal'].as_dict(), cal)
 
-        self.assertRaises(ParserError, lambda: parser.parse_args(['--cal={"class_path":"not.exist.Class"}']))
-        self.assertRaises(ParserError, lambda: parser.parse_args(['--cal={"class_path":"calendar.January"}']))
-        self.assertRaises(ParserError, lambda: parser.parse_args(['--cal.help=calendar.January']))
-        self.assertRaises(ParserError, lambda: parser.parse_args(['--cal.help=calendar.does_not_exist']))
+        self.assertRaises(ArgumentError, lambda: parser.parse_args(['--cal={"class_path":"not.exist.Class"}']))
+        self.assertRaises(ArgumentError, lambda: parser.parse_args(['--cal={"class_path":"calendar.January"}']))
+        self.assertRaises(ArgumentError, lambda: parser.parse_args(['--cal.help=calendar.January']))
+        self.assertRaises(ArgumentError, lambda: parser.parse_args(['--cal.help=calendar.does_not_exist']))
         self.assertRaises(ValueError, lambda: parser.add_subclass_arguments(January, 'jan'))
 
         out = StringIO()
@@ -478,13 +478,13 @@ class SignaturesTests(unittest.TestCase):
             self.assertIn('--cal.init_args.firstweekday', out.getvalue())
             self.assertNotIn('param', out.getvalue())
 
-            parser = ArgumentParser(error_handler=None)
+            parser = ArgumentParser(exit_on_error=False)
             parser.add_subclass_arguments(Calendar, 'cal')
             self.assertRaises(ValueError, lambda: parser.parse_args(args))
 
 
     def test_add_subclass_discard_init_args(self):
-        parser = ArgumentParser(error_handler=None, logger={'level': 'DEBUG'})
+        parser = ArgumentParser(exit_on_error=False, logger={'level': 'DEBUG'})
         parser.add_subclass_arguments(Calendar, 'cal')
 
         class CalA(Calendar):
@@ -525,7 +525,7 @@ class SignaturesTests(unittest.TestCase):
             def __init__(self, c: ChildBase):
                 pass
 
-        parser = ArgumentParser(error_handler=None, logger={'level': 'DEBUG'})
+        parser = ArgumentParser(exit_on_error=False, logger={'level': 'DEBUG'})
         with mock_module(Parent, ChildBase, A, B) as module, self.assertLogs(logger=parser.logger, level='DEBUG') as log:
             parser.add_subclass_arguments(Parent, 'p')
             cfg = parser.parse_args([
@@ -550,7 +550,7 @@ class SignaturesTests(unittest.TestCase):
             def __init__(self, cal: Union[Calendar, bool] = lazy_instance(MyCalendar, param=1)):
                 self.cal = cal
 
-        parser = ArgumentParser(error_handler=None, logger={'level': 'DEBUG'})
+        parser = ArgumentParser(exit_on_error=False, logger={'level': 'DEBUG'})
         with mock_module(MyCalendar), self.assertLogs(logger=parser.logger, level='DEBUG') as log:
             parser.add_class_arguments(Main, 'main')
             parser.parse_args(['--main.cal=Calendar'])
@@ -558,12 +558,12 @@ class SignaturesTests(unittest.TestCase):
 
 
     def test_add_subclass_init_args_without_class_path(self):
-        parser = ArgumentParser(error_handler=None)
+        parser = ArgumentParser(exit_on_error=False)
         parser.add_subclass_arguments(Calendar, 'cal1')
         parser.add_subclass_arguments(Calendar, 'cal2', default=lazy_instance(Calendar))
         parser.add_subclass_arguments(Calendar, 'cal3', default=lazy_instance(Calendar, firstweekday=2))
 
-        self.assertRaises(ParserError, lambda: parser.parse_args(['--cal1.init_args.firstweekday=4']))
+        self.assertRaises(ArgumentError, lambda: parser.parse_args(['--cal1.init_args.firstweekday=4']))
         cfg = parser.parse_args(['--cal2.init_args.firstweekday=4', '--cal3.init_args.firstweekday=5'])
         self.assertEqual(cfg.cal2.init_args, Namespace(firstweekday=4))
         self.assertEqual(cfg.cal3.init_args, Namespace(firstweekday=5))
@@ -580,7 +580,7 @@ class SignaturesTests(unittest.TestCase):
                 self.image_size = image_size
 
         with mock_module(ModelBaseClass, ModelClass) as module:
-            parser = ArgumentParser(error_handler=None)
+            parser = ArgumentParser(exit_on_error=False)
             parser.add_subclass_arguments(ModelBaseClass, 'model')
             parser.add_argument('--config', action=ActionConfigFile)
 
@@ -604,7 +604,7 @@ class SignaturesTests(unittest.TestCase):
 
 
     def test_add_subclass_init_args_in_subcommand(self):
-        parser = ArgumentParser(error_handler=None)
+        parser = ArgumentParser(exit_on_error=False)
         subcommands = parser.add_subcommands()
         subparser = ArgumentParser()
         subparser.add_subclass_arguments(Calendar, 'cal', default=lazy_instance(Calendar))
@@ -630,7 +630,7 @@ class SignaturesTests(unittest.TestCase):
             class_path_a = f'{module}.ClassA'
             class_path_b = f'{module}.ClassB'
 
-            parser = ArgumentParser(error_handler=None)
+            parser = ArgumentParser(exit_on_error=False)
             parser.add_subclass_arguments((ClassA, ClassB), 'c')
 
             cfg = parser.parse_args(['--c={"class_path": "'+class_path_a+'", "init_args": {"a1": -1}}'])
@@ -651,17 +651,17 @@ class SignaturesTests(unittest.TestCase):
 
 
     def test_required_group(self):
-        parser = ArgumentParser(error_handler=None)
+        parser = ArgumentParser(exit_on_error=False)
         self.assertRaises(ValueError, lambda: parser.add_subclass_arguments(Calendar, None, required=True))
         parser.add_subclass_arguments(Calendar, 'cal', required=True)
-        self.assertRaises(ParserError, lambda: parser.parse_args([]))
+        self.assertRaises(ArgumentError, lambda: parser.parse_args([]))
         out = StringIO()
         parser.print_help(out)
         self.assertIn('[-h] [--cal.help CLASS_PATH_OR_NAME] --cal ', out.getvalue())
 
 
     def test_not_required_group(self):
-        parser = ArgumentParser(error_handler=None)
+        parser = ArgumentParser(exit_on_error=False)
         parser.add_subclass_arguments(Calendar, 'cal', required=False)
         cfg = parser.parse_args([])
         self.assertEqual(cfg, Namespace())
@@ -674,7 +674,7 @@ class SignaturesTests(unittest.TestCase):
         def func(a1: None):
             return a1
 
-        parser = ArgumentParser(error_handler=None)
+        parser = ArgumentParser(exit_on_error=False)
         self.assertRaises(ValueError, lambda: parser.add_function_arguments(func))
 
 
@@ -688,10 +688,10 @@ class SignaturesTests(unittest.TestCase):
         def func(a1: Optional[MyEnum] = None):
             return a1
 
-        parser = ArgumentParser(error_handler=None)
+        parser = ArgumentParser(exit_on_error=False)
         parser.add_function_arguments(func)
         self.assertEqual(MyEnum.B, parser.parse_args(['--a1=B']).a1)
-        self.assertRaises(ParserError, lambda: parser.parse_args(['--a1=D']))
+        self.assertRaises(ArgumentError, lambda: parser.parse_args(['--a1=D']))
 
         help_str = StringIO()
         parser.print_help(help_str)
@@ -704,7 +704,7 @@ class SignaturesTests(unittest.TestCase):
         def func2(a1: Optional[MyEnum2] = None):
             return a1
 
-        parser = ArgumentParser(error_handler=None)
+        parser = ArgumentParser(exit_on_error=False)
         parser.add_function_arguments(func2)
         self.assertEqual(MyEnum2.B, parser.parse_args(['--a1=B']).a1)
         self.assertEqual('B', parser.parse_args(['--a1=B']).a1)
@@ -719,7 +719,7 @@ class SignaturesTests(unittest.TestCase):
         def func(a1: Any = MyEnum.B):
             return a1
 
-        parser = ArgumentParser(error_handler=None)
+        parser = ArgumentParser(exit_on_error=False)
         parser.add_function_arguments(func)
         cfg = parser.parse_args([])
         self.assertEqual('a1: B\n', parser.dump(cfg))
@@ -761,7 +761,7 @@ class SignaturesTests(unittest.TestCase):
             def __init__(self, c1: Class1, c2: int = 5, c3: float = 6.7):
                 pass
 
-        parser = ArgumentParser(error_handler=None)
+        parser = ArgumentParser(exit_on_error=False)
         parser.add_class_arguments(Class2, skip={'c1.init_args.a2', 'c2'})
 
         with mock_module(Class1) as module:
@@ -782,7 +782,7 @@ class SignaturesTests(unittest.TestCase):
                 self.b1 = b1
                 self.b2 = b2
 
-        parser = ArgumentParser(error_handler=None)
+        parser = ArgumentParser(exit_on_error=False)
         parser.add_subclass_arguments(ClassA, 'c', skip={'a1', 'b2'})
 
         with mock_module(ClassA, ClassB) as module:
@@ -808,7 +808,7 @@ class SignaturesTests(unittest.TestCase):
                 self.b1 = b1
                 self.b2 = b2
 
-        parser = ArgumentParser(error_handler=None)
+        parser = ArgumentParser(exit_on_error=False)
         parser.add_class_arguments(ClassB, 'b')
 
         self.assertEqual(parser.get_defaults().b.b2, Namespace(a1=1, a2=-3.2))
@@ -819,8 +819,8 @@ class SignaturesTests(unittest.TestCase):
         self.assertIsInstance(cfg['b'], ClassB)
         self.assertIsInstance(cfg['b'].b2, ClassA)
 
-        self.assertRaises(ParserError, lambda: parser.parse_args(['--b.b2={"bad": "value"}']))
-        self.assertRaises(ParserError, lambda: parser.parse_args(['--b.b2="bad"']))
+        self.assertRaises(ArgumentError, lambda: parser.parse_args(['--b.b2={"bad": "value"}']))
+        self.assertRaises(ArgumentError, lambda: parser.parse_args(['--b.b2="bad"']))
         self.assertRaises(ValueError, lambda: parser.add_subclass_arguments(ClassA, 'a'))
         self.assertRaises(ValueError, lambda: parser.add_class_arguments(ClassA, 'a', default=ClassA()))
 
@@ -831,18 +831,18 @@ class SignaturesTests(unittest.TestCase):
                  a2: Optional[Union[PositiveInt, OpenUnitInterval]] = 0.5):
             return a1, a2
 
-        parser = ArgumentParser(error_handler=None)
+        parser = ArgumentParser(exit_on_error=False)
         parser.add_function_arguments(func)
 
         self.assertEqual(1.0, parser.parse_args(['--a1=1']).a1)
-        self.assertRaises(ParserError, lambda: parser.parse_args(['--a1=-1']))
+        self.assertRaises(ArgumentError, lambda: parser.parse_args(['--a1=-1']))
 
         self.assertEqual(0.7, parser.parse_args(['--a2=0.7']).a2)
         self.assertEqual(5, parser.parse_args(['--a2=5']).a2)
         self.assertEqual(None, parser.parse_args(['--a2=null']).a2)
-        self.assertRaises(ParserError, lambda: parser.parse_args(['--a2=0']))
-        self.assertRaises(ParserError, lambda: parser.parse_args(['--a2=1.5']))
-        self.assertRaises(ParserError, lambda: parser.parse_args(['--a2=-1']))
+        self.assertRaises(ArgumentError, lambda: parser.parse_args(['--a2=0']))
+        self.assertRaises(ArgumentError, lambda: parser.parse_args(['--a2=1.5']))
+        self.assertRaises(ArgumentError, lambda: parser.parse_args(['--a2=-1']))
 
 
     def test_dict_int_str_type(self):
@@ -850,12 +850,12 @@ class SignaturesTests(unittest.TestCase):
             def __init__(self, d: Dict[int, str]):
                 self.d = d
 
-        parser = ArgumentParser(error_handler=None)
+        parser = ArgumentParser(exit_on_error=False)
         parser.add_class_arguments(Foo)
         parser.add_argument('--config', action=ActionConfigFile)
         cfg = {'d': {1: 'val1', 2: 'val2'}}
         self.assertEqual(cfg['d'], parser.parse_args(['--config', str(cfg)]).d)
-        self.assertRaises(ParserError, lambda: parser.parse_args(['--config={"d": {"a": "b"}}']))
+        self.assertRaises(ArgumentError, lambda: parser.parse_args(['--config={"d": {"a": "b"}}']))
 
 
     def test_logger_debug(self):
@@ -876,7 +876,7 @@ class SignaturesTests(unittest.TestCase):
                             **kwargs):
                     pass
 
-            parser = ArgumentParser(error_handler=None, logger={'level': 'DEBUG'})
+            parser = ArgumentParser(exit_on_error=False, logger={'level': 'DEBUG'})
             with self.assertLogs(logger=parser.logger, level='DEBUG') as log:
                 parser.add_class_arguments(Class2, skip={'c2_a2'})
                 self.assertEqual(1, len(log.output))
@@ -894,7 +894,7 @@ class SignaturesTests(unittest.TestCase):
             def __init__(self, c1: Optional[Class1]):
                 self.c1 = c1
 
-        parser = ArgumentParser(error_handler=None)
+        parser = ArgumentParser(exit_on_error=False)
         parser.add_class_arguments(Class2)
 
         with mock_module(Class1) as module:
@@ -908,7 +908,7 @@ class SignaturesTests(unittest.TestCase):
             self.assertEqual(7, cfg['c1'].a1)
             self.assertEqual(2.3, cfg['c1'].a2)
 
-            parser = ArgumentParser(error_handler=None)
+            parser = ArgumentParser(exit_on_error=False)
             parser.add_class_arguments(Class2, 'c2')
 
             cfg = parser.parse_args(['--c2={"c1": {'+class_path+', '+init_args+'}}'])
@@ -919,7 +919,7 @@ class SignaturesTests(unittest.TestCase):
             class EmptyInitClass:
                 pass
 
-            parser = ArgumentParser(error_handler=None)
+            parser = ArgumentParser(exit_on_error=False)
             parser.add_class_arguments(EmptyInitClass, 'e')
             cfg = parser.parse_args([])
             cfg = parser.instantiate_classes(cfg)
@@ -949,7 +949,7 @@ class SignaturesTests(unittest.TestCase):
         def func(a1: Optional[int] = None):
             return a1
 
-        parser = ArgumentParser(error_handler=None)
+        parser = ArgumentParser(exit_on_error=False)
         parser.add_function_arguments(func)
 
         self.assertIsNone(parser.parse_args(['--a1=null']).a1)
@@ -960,7 +960,7 @@ class SignaturesTests(unittest.TestCase):
         def func(a1, a2=None):
             return a1
 
-        parser = ArgumentParser(error_handler=None)
+        parser = ArgumentParser(exit_on_error=False)
         added_args = parser.add_function_arguments(func, fail_untyped=False)
 
         self.assertEqual(['a1', 'a2'], added_args)
@@ -976,7 +976,7 @@ class SignaturesTests(unittest.TestCase):
             return c1
 
         with mock_module(Class1) as module:
-            parser = ArgumentParser(error_handler=None)
+            parser = ArgumentParser(exit_on_error=False)
             parser.add_function_arguments(func, fail_untyped=False)
 
             help_str = StringIO()
@@ -998,7 +998,7 @@ class SignaturesTests(unittest.TestCase):
         with unittest.mock.patch('docstring_parser.parse') as docstring_parse:
             from docstring_parser import ParseError
             docstring_parse.side_effect = ParseError
-            parser = ArgumentParser(error_handler=None)
+            parser = ArgumentParser(exit_on_error=False)
             parser.add_class_arguments(Class1)
 
             help_str = StringIO()
@@ -1047,7 +1047,7 @@ class SignaturesTests(unittest.TestCase):
             def __init__(self, arg1: int, arg2: int = 1):
                 pass
 
-        parser = ArgumentParser(error_handler=None)
+        parser = ArgumentParser(exit_on_error=False)
         parser.add_argument("--config", action=ActionConfigFile)
         parser.add_class_arguments(Class, 'class')
         parser.add_subclass_arguments(BaseClass, 'subclass')
@@ -1106,7 +1106,7 @@ class SignaturesTests(unittest.TestCase):
 
         get_calendar_class = class_from_function(get_calendar)
 
-        parser = ArgumentParser(error_handler=None)
+        parser = ArgumentParser(exit_on_error=False)
         parser.add_class_arguments(get_calendar_class, 'a')
 
         if docstring_parser_support:
@@ -1152,7 +1152,7 @@ class SignaturesTests(unittest.TestCase):
                         class_path: {module}.Module
             """
 
-            parser = ArgumentParser(error_handler=None)
+            parser = ArgumentParser(exit_on_error=False)
             parser.add_argument('--config', action=ActionConfigFile)
             parser.add_class_arguments(Model, 'model')
 
@@ -1197,7 +1197,7 @@ class SignaturesConfigTests(TempDirTestCase):
                  a3: bool = False):
             return a1
 
-        parser = ArgumentParser(error_handler=None, default_meta=False)
+        parser = ArgumentParser(exit_on_error=False, default_meta=False)
         parser.add_function_arguments(func, 'func')
 
         cfg_path = 'config.yaml'
@@ -1210,7 +1210,7 @@ class SignaturesConfigTests(TempDirTestCase):
         cfg = parser.parse_args(['--func={"a1": "ONE"}'])
         self.assertEqual(cfg.func, Namespace(a1='ONE', a2=2.0, a3=False))
 
-        self.assertRaises(ParserError, lambda: parser.parse_args(['--func="""']))
+        self.assertRaises(ArgumentError, lambda: parser.parse_args(['--func="""']))
 
 
     def test_config_within_config(self):
@@ -1220,7 +1220,7 @@ class SignaturesConfigTests(TempDirTestCase):
                  a3: bool = False):
             return a1
 
-        parser = ArgumentParser(error_handler=None)
+        parser = ArgumentParser(exit_on_error=False)
         parser.add_argument('--cfg', action=ActionConfigFile)
         parser.add_function_arguments(func, 'func')
 
@@ -1239,7 +1239,7 @@ class SignaturesConfigTests(TempDirTestCase):
 
 
     def test_add_subclass_arguments_with_config(self):
-        parser = ArgumentParser(error_handler=None)
+        parser = ArgumentParser(exit_on_error=False)
         parser.add_argument('--cfg', action=ActionConfigFile)
         parser.add_subclass_arguments(Calendar, 'cal')
 
@@ -1255,7 +1255,7 @@ class SignaturesConfigTests(TempDirTestCase):
         cfg = parser.parse_args(['--cfg='+cfg_path, '--cal.init_args.firstweekday=2'])
         self.assertEqual(cfg['cal'].as_dict(), cal)
 
-        parser = ArgumentParser(error_handler=None, default_config_files=['config.yaml'])
+        parser = ArgumentParser(exit_on_error=False, default_config_files=['config.yaml'])
         parser.add_subclass_arguments(Calendar, 'cal')
 
         cfg = parser.parse_args(['--cal.init_args.firstweekday=2'])
@@ -1267,18 +1267,18 @@ class SignaturesConfigTests(TempDirTestCase):
             def __init__(self, param: int):
                 self.param = param
 
-        parser = ArgumentParser(error_handler=None)
+        parser = ArgumentParser(exit_on_error=False)
         parser.add_class_arguments(A, 'a')
         try:
             parser.parse_args(['--a=does_not_exist.yaml'])
-        except ParserError as ex:
+        except ArgumentError as ex:
             self.assertIn('Unable to load config "does_not_exist.yaml"', str(ex))
         else:
-            raise ValueError('Expected ParserError to be raised')
+            raise ValueError('Expected ArgumentError to be raised')
 
 
     def test_add_subclass_arguments_with_multifile_save(self):
-        parser = ArgumentParser(error_handler=None)
+        parser = ArgumentParser(exit_on_error=False)
         parser.add_subclass_arguments(Calendar, 'cal')
 
         cal_cfg_path = 'cal.yaml'
@@ -1322,7 +1322,7 @@ class SignaturesConfigTests(TempDirTestCase):
             with open('defaults.yaml', 'w') as f:
                 f.write(defaults)
 
-            parser = ArgumentParser(error_handler=None, default_config_files=['defaults.yaml'])
+            parser = ArgumentParser(exit_on_error=False, default_config_files=['defaults.yaml'])
             parser.add_class_arguments(Model, 'model')
 
             cfg = parser.parse_args(['--model.sub_module.init_args.p2=7'])
@@ -1339,7 +1339,7 @@ class SignaturesConfigTests(TempDirTestCase):
             def __init__(self, foo: int):
                 self.foo = foo
 
-        parser = ArgumentParser(default_config_files=["default.yaml"], error_handler=None)
+        parser = ArgumentParser(default_config_files=["default.yaml"], exit_on_error=False)
         parser.add_argument('--config', action=ActionConfigFile)
         subcommands = parser.add_subcommands()
 
@@ -1382,7 +1382,7 @@ class SignaturesConfigTests(TempDirTestCase):
 
             for subtest in ['class', 'subclass']:
                 with self.subTest(subtest):
-                    parser = ArgumentParser(error_handler=None, logger={'level': 'DEBUG'})
+                    parser = ArgumentParser(exit_on_error=False, logger={'level': 'DEBUG'})
                     parser.add_argument('--config', action=ActionConfigFile)
 
                     if subtest == 'class':
@@ -1436,7 +1436,7 @@ class DataclassesTests(unittest.TestCase):
 
     @unittest.skipIf(not docstring_parser_support, 'docstring-parser package is required')
     def test_add_dataclass_arguments(self):
-        parser = ArgumentParser(error_handler=None)
+        parser = ArgumentParser(exit_on_error=False)
         parser.add_dataclass_arguments(MyDataClassA, 'a', default=MyDataClassA(), title='CustomA title')
         parser.add_dataclass_arguments(MyDataClassB, 'b', default=MyDataClassB())
 
@@ -1453,7 +1453,7 @@ class DataclassesTests(unittest.TestCase):
         self.assertIsInstance(cfg['b'].b2, MyDataClassA)
 
         self.assertEqual(5, parser.parse_args(['--b.b2.a1=5']).b.b2.a1)
-        self.assertRaises(ParserError, lambda: parser.parse_args(['--b.b2.a1=x']))
+        self.assertRaises(ArgumentError, lambda: parser.parse_args(['--b.b2.a1=x']))
 
         help_str = StringIO()
         parser.print_help(help_str)
@@ -1513,7 +1513,7 @@ class DataclassesTests(unittest.TestCase):
                 """MyClass2 description"""
                 self.c1 = c1
 
-        parser = ArgumentParser(error_handler=None)
+        parser = ArgumentParser(exit_on_error=False)
         parser.add_class_arguments(MyClass2)
 
         with mock_module(MyClass1, MyClass2) as module:
@@ -1549,11 +1549,11 @@ class DataclassesTests(unittest.TestCase):
             a1: str
             a2: float = 1.2
 
-        parser = ArgumentParser(error_handler=None)
+        parser = ArgumentParser(exit_on_error=False)
         parser.add_argument('--b', type=MyDataClass)
 
         self.assertEqual(Namespace(a1='v', a2=1.2), parser.parse_args(['--b.a1=v']).b)
-        self.assertRaises(ParserError, lambda: parser.parse_args([]))
+        self.assertRaises(ArgumentError, lambda: parser.parse_args([]))
 
 
     def test_dataclass_field_default_factory(self):
diff --git a/jsonargparse_tests/test_stubs_resolver.py b/jsonargparse_tests/test_stubs_resolver.py
index 6633bcb..a37a963 100755
--- a/jsonargparse_tests/test_stubs_resolver.py
+++ b/jsonargparse_tests/test_stubs_resolver.py
@@ -179,7 +179,7 @@ class StubsResolverTests(unittest.TestCase):
         if sys.version_info >= (3, 10):
             self.assertTrue(all(p.annotation != inspect._empty for p in params))
 
-        parser = ArgumentParser(error_handler=None)
+        parser = ArgumentParser(exit_on_error=False)
         parser.add_function_arguments(get, fail_untyped=False)
         self.assertEqual(['url', 'params'], list(parser.get_defaults().keys()))
         help_str = StringIO()
diff --git a/jsonargparse_tests/test_typehints.py b/jsonargparse_tests/test_typehints.py
index 19f7c09..653e58d 100755
--- a/jsonargparse_tests/test_typehints.py
+++ b/jsonargparse_tests/test_typehints.py
@@ -36,9 +36,9 @@ import yaml
 from jsonargparse import (
     CLI,
     ActionConfigFile,
+    ArgumentError,
     ArgumentParser,
     Namespace,
-    ParserError,
     Path,
     lazy_instance,
 )
@@ -63,34 +63,34 @@ from jsonargparse_tests.base import TempDirTestCase, mock_module
 class TypeHintsTests(unittest.TestCase):
 
     def test_add_argument_type_hint(self):
-        parser = ArgumentParser(error_handler=None)
+        parser = ArgumentParser(exit_on_error=False)
         parser.add_argument('--op1', type=Optional[Union[PositiveInt, OpenUnitInterval]])
         self.assertEqual(0.1, parser.parse_args(['--op1', '0.1']).op1)
         self.assertEqual(0.9, parser.parse_args(['--op1', '0.9']).op1)
         self.assertEqual(1, parser.parse_args(['--op1', '1']).op1)
         self.assertEqual(12, parser.parse_args(['--op1', '12']).op1)
-        self.assertRaises(ParserError, lambda: parser.parse_args(['--op1', '0.0']))
-        self.assertRaises(ParserError, lambda: parser.parse_args(['--op1', '4.5']))
+        self.assertRaises(ArgumentError, lambda: parser.parse_args(['--op1', '0.0']))
+        self.assertRaises(ArgumentError, lambda: parser.parse_args(['--op1', '4.5']))
         parser.add_argument('--op2', type=Optional[Email])
         self.assertEqual('a@b.c', parser.parse_args(['--op2', 'a@b.c']).op2)
         self.assertIsNone(parser.parse_args(['--op2=null']).op2)
-        self.assertRaises(ParserError, lambda: parser.parse_args(['--op2', 'abc']))
+        self.assertRaises(ArgumentError, lambda: parser.parse_args(['--op2', 'abc']))
 
 
     def test_type_hint_action_failure(self):
-        parser = ArgumentParser(error_handler=None)
+        parser = ArgumentParser(exit_on_error=False)
         self.assertRaises(ValueError, lambda: parser.add_argument('--op1', type=Optional[bool], action=True))
 
 
     def test_bool(self):
-        parser = ArgumentParser(prog='app', default_env=True, error_handler=None)
+        parser = ArgumentParser(prog='app', default_env=True, exit_on_error=False)
         parser.add_argument('--val', type=bool)
         self.assertEqual(None,  parser.get_defaults().val)
         self.assertEqual(True,  parser.parse_args(['--val', 'true']).val)
         self.assertEqual(True,  parser.parse_args(['--val', 'TRUE']).val)
         self.assertEqual(False, parser.parse_args(['--val', 'false']).val)
         self.assertEqual(False, parser.parse_args(['--val', 'FALSE']).val)
-        self.assertRaises(ParserError, lambda: parser.parse_args(['--val', '1']))
+        self.assertRaises(ArgumentError, lambda: parser.parse_args(['--val', '1']))
 
         os.environ['APP_VAL'] = 'true'
         self.assertEqual(True,  parser.parse_args([]).val)
@@ -101,12 +101,12 @@ class TypeHintsTests(unittest.TestCase):
         os.environ['APP_VAL'] = 'False'
         self.assertEqual(False, parser.parse_args([]).val)
         os.environ['APP_VAL'] = '2'
-        self.assertRaises(ParserError, lambda: parser.parse_args(['--val', 'a']))
+        self.assertRaises(ArgumentError, lambda: parser.parse_args(['--val', 'a']))
         del os.environ['APP_VAL']
 
 
     def test_no_str_strip(self):
-        parser = ArgumentParser(error_handler=None)
+        parser = ArgumentParser(exit_on_error=False)
         parser.add_argument('--op', type=Optional[str])
         parser.add_argument('--cfg', action=ActionConfigFile)
         self.assertEqual('  ', parser.parse_args(['--op', '  ']).op)
@@ -149,10 +149,10 @@ class TypeHintsTests(unittest.TestCase):
     def test_boolean_not_a_number(self):
         for argtype in [int, float, PositiveInt, PositiveFloat]:
             with self.subTest(argtype):
-                parser = ArgumentParser(error_handler=None)
+                parser = ArgumentParser(exit_on_error=False)
                 parser.add_argument('--num', type=argtype)
                 for value in [True, False]:
-                    with self.assertRaises(ParserError):
+                    with self.assertRaises(ArgumentError):
                         parser.parse_object({'num': value})
 
 
@@ -177,13 +177,13 @@ class TypeHintsTests(unittest.TestCase):
             B = 2
             C = 3
 
-        parser = ArgumentParser(error_handler=None)
+        parser = ArgumentParser(exit_on_error=False)
         parser.add_argument('--enum', type=MyEnum, default=MyEnum.C, help='Description')
 
         for val in ['A', 'B', 'C']:
             self.assertEqual(MyEnum[val], parser.parse_args(['--enum='+val]).enum)
         for val in ['X', 'b', 2]:
-            self.assertRaises(ParserError, lambda: parser.parse_args(['--enum='+str(val)]))
+            self.assertRaises(ArgumentError, lambda: parser.parse_args(['--enum='+str(val)]))
 
         cfg = parser.parse_args(['--enum=C'], with_meta=False)
         self.assertEqual('enum: C\n', parser.dump(cfg))
@@ -198,7 +198,7 @@ class TypeHintsTests(unittest.TestCase):
             ab = 0
             xy = 1
 
-        parser = ArgumentParser(error_handler=None)
+        parser = ArgumentParser(exit_on_error=False)
         parser.add_argument('--list', type=List[MyEnum])
         self.assertEqual([MyEnum.xy, MyEnum.ab], parser.parse_args(['--list=["xy", "ab"]']).list)
 
@@ -207,24 +207,24 @@ class TypeHintsTests(unittest.TestCase):
         class MyEnum(Enum):
             ab = 1
 
-        parser = ArgumentParser(error_handler=None)
+        parser = ArgumentParser(exit_on_error=False)
         parser.add_argument('--list1', type=List[Union[float, str, type(None)]])
         parser.add_argument('--list2', type=List[Union[int, MyEnum]])
         self.assertEqual([1.2, 'ab'], parser.parse_args(['--list1=[1.2, "ab"]']).list1)
         self.assertEqual([3, MyEnum.ab], parser.parse_args(['--list2=[3, "ab"]']).list2)
-        self.assertRaises(ParserError, lambda: parser.parse_args(['--list1={"a":1, "b":"2"}']))
+        self.assertRaises(ArgumentError, lambda: parser.parse_args(['--list1={"a":1, "b":"2"}']))
 
 
     def test_dict(self):
-        parser = ArgumentParser(error_handler=None)
+        parser = ArgumentParser(exit_on_error=False)
         parser.add_argument('--dict', type=dict)
         self.assertEqual({}, parser.parse_args(['--dict={}'])['dict'])
         self.assertEqual({'a': 1, 'b': '2'}, parser.parse_args(['--dict={"a":1, "b":"2"}'])['dict'])
-        self.assertRaises(ParserError, lambda: parser.parse_args(['--dict=1']))
+        self.assertRaises(ArgumentError, lambda: parser.parse_args(['--dict=1']))
 
 
     def test_dict_items(self):
-        parser = ArgumentParser(error_handler=None)
+        parser = ArgumentParser(exit_on_error=False)
         parser.add_argument('--dict', type=Dict[str, int])
         cfg = parser.parse_args(['--dict.one=1', '--dict.two=2'])
         self.assertEqual(cfg.dict, {'one': 1, 'two': 2})
@@ -234,7 +234,7 @@ class TypeHintsTests(unittest.TestCase):
         class MyEnum(Enum):
             ab = 1
 
-        parser = ArgumentParser(error_handler=None)
+        parser = ArgumentParser(exit_on_error=False)
         parser.add_argument('--dict1', type=Dict[int, Optional[Union[float, MyEnum]]])
         parser.add_argument('--dict2', type=Dict[str, Union[bool, Path_fc]])
         cfg = parser.parse_args(['--dict1={"2":4.5, "6":"ab"}', '--dict2={"a":true, "b":"f"}'])
@@ -242,38 +242,38 @@ class TypeHintsTests(unittest.TestCase):
         self.assertEqual({'a': True, 'b': 'f'}, cfg['dict2'])
         self.assertIsInstance(cfg['dict2']['b'], Path)
         self.assertEqual({5: None}, parser.parse_args(['--dict1={"5":null}'])['dict1'])
-        self.assertRaises(ParserError, lambda: parser.parse_args(['--dict1=["a", "b"]']))
+        self.assertRaises(ArgumentError, lambda: parser.parse_args(['--dict1=["a", "b"]']))
         cfg = yaml.safe_load(parser.dump(cfg))
         self.assertEqual({'dict1': {'2': 4.5, '6': 'ab'}, 'dict2': {'a': True, 'b': 'f'}}, cfg)
 
 
     def test_set(self):
-        parser = ArgumentParser(error_handler=None)
+        parser = ArgumentParser(exit_on_error=False)
         parser.add_argument('--set', type=Set[int])
         self.assertEqual({1, 2}, parser.parse_args(['--set=[1, 2]']).set)
-        self.assertRaises(ParserError, lambda: parser.parse_args(['--set=["a", "b"]']))
+        self.assertRaises(ArgumentError, lambda: parser.parse_args(['--set=["a", "b"]']))
 
 
     def test_tuple(self):
         class MyEnum(Enum):
             ab = 1
 
-        parser = ArgumentParser(error_handler=None)
+        parser = ArgumentParser(exit_on_error=False)
         parser.add_argument('--tuple', type=Tuple[Union[int, MyEnum], Path_fc, NotEmptyStr])
         cfg = parser.parse_args(['--tuple=[2, "a", "b"]'])
         self.assertEqual((2, 'a', 'b'), cfg.tuple)
         self.assertIsInstance(cfg.tuple[1], Path)
-        self.assertRaises(ParserError, lambda: parser.parse_args(['--tuple=[]']))
-        self.assertRaises(ParserError, lambda: parser.parse_args(['--tuple=[2, "a", "b", 5]']))
-        self.assertRaises(ParserError, lambda: parser.parse_args(['--tuple=[2, "a"]']))
-        self.assertRaises(ParserError, lambda: parser.parse_args(['--tuple={"a":1, "b":"2"}']))
+        self.assertRaises(ArgumentError, lambda: parser.parse_args(['--tuple=[]']))
+        self.assertRaises(ArgumentError, lambda: parser.parse_args(['--tuple=[2, "a", "b", 5]']))
+        self.assertRaises(ArgumentError, lambda: parser.parse_args(['--tuple=[2, "a"]']))
+        self.assertRaises(ArgumentError, lambda: parser.parse_args(['--tuple={"a":1, "b":"2"}']))
         out = StringIO()
         parser.print_help(out)
         self.assertIn('--tuple [ITEM,...]  (type: Tuple[Union[int, MyEnum], Path_fc, NotEmptyStr], default: null)', out.getvalue())
 
 
     def test_tuple_untyped(self):
-        parser = ArgumentParser(error_handler=None)
+        parser = ArgumentParser(exit_on_error=False)
         parser.add_argument('--tuple', type=tuple)
         cfg = parser.parse_args(['--tuple=[1, "a", True]'])
         self.assertEqual((1, 'a', True), cfg.tuple)
@@ -283,21 +283,21 @@ class TypeHintsTests(unittest.TestCase):
 
 
     def test_nested_tuples(self):
-        parser = ArgumentParser(error_handler=None)
+        parser = ArgumentParser(exit_on_error=False)
         parser.add_argument('--tuple', type=Tuple[Tuple[str, str], Tuple[Tuple[int, float], Tuple[int, float]]])
         cfg = parser.parse_args(['--tuple=[["foo", "bar"], [[1, 2.02], [3, 3.09]]]'])
         self.assertEqual((('foo', 'bar'), ((1, 2.02), (3, 3.09))), cfg.tuple)
 
 
     def test_list_tuple(self):
-        parser = ArgumentParser(error_handler=None)
+        parser = ArgumentParser(exit_on_error=False)
         parser.add_argument('--list', type=List[Tuple[int, float]])
         cfg = parser.parse_args(['--list=[[1, 2.02], [3, 3.09]]'])
         self.assertEqual([(1, 2.02), (3, 3.09)], cfg.list)
 
 
     def test_list_str_positional(self):
-        parser = ArgumentParser(error_handler=None)
+        parser = ArgumentParser(exit_on_error=False)
         parser.add_argument('list', type=List[str])
         cfg = parser.parse_args(['["a", "b"]'])
         self.assertEqual(cfg.list, ['a', 'b'])
@@ -311,21 +311,21 @@ class TypeHintsTests(unittest.TestCase):
 
 
     def test_tuple_ellipsis(self):
-        parser = ArgumentParser(error_handler=None)
+        parser = ArgumentParser(exit_on_error=False)
         parser.add_argument('--tuple', type=Tuple[float, ...])
         self.assertEqual((1.2,), parser.parse_args(['--tuple=[1.2]']).tuple)
         self.assertEqual((1.2, 3.4), parser.parse_args(['--tuple=[1.2, 3.4]']).tuple)
-        self.assertRaises(ParserError, lambda: parser.parse_args(['--tuple=[]']))
-        self.assertRaises(ParserError, lambda: parser.parse_args(['--tuple=[2, "a"]']))
+        self.assertRaises(ArgumentError, lambda: parser.parse_args(['--tuple=[]']))
+        self.assertRaises(ArgumentError, lambda: parser.parse_args(['--tuple=[2, "a"]']))
 
-        parser = ArgumentParser(error_handler=None)
+        parser = ArgumentParser(exit_on_error=False)
         parser.add_argument('--tuple', type=Tuple[Tuple[str, str], Tuple[Tuple[int, float], ...]])
         cfg = parser.parse_args(['--tuple=[["foo", "bar"], [[1, 2.02], [3, 3.09]]]'])
         self.assertEqual((('foo', 'bar'), ((1, 2.02), (3, 3.09))), cfg.tuple)
 
 
     def test_complex_number(self):
-        parser = ArgumentParser(error_handler=None)
+        parser = ArgumentParser(exit_on_error=False)
         parser.add_argument('--complex', type=complex)
         cfg = parser.parse_args(['--complex=(2+3j)'])
         self.assertEqual(cfg.complex, 2+3j)
@@ -333,7 +333,7 @@ class TypeHintsTests(unittest.TestCase):
 
 
     def test_list_append(self):
-        parser = ArgumentParser(error_handler=None)
+        parser = ArgumentParser(exit_on_error=False)
         parser.add_argument('--val', type=Union[int, float, List[int]])
         self.assertEqual(0, parser.parse_args(['--val=0']).val)
         self.assertEqual([0], parser.parse_args(['--val+=0']).val)
@@ -341,7 +341,7 @@ class TypeHintsTests(unittest.TestCase):
         self.assertEqual([1, 2, 3], parser.parse_args(['--val=[1,2]', '--val+=3']).val)
         self.assertEqual([1], parser.parse_args(['--val=0.1', '--val+=1']).val)
         self.assertEqual(3, parser.parse_args(['--val=[1,2]', '--val=3']).val)
-        self.assertRaises(ParserError, lambda: parser.parse_args(['--val=a', '--val+=1']))
+        self.assertRaises(ArgumentError, lambda: parser.parse_args(['--val=a', '--val+=1']))
 
 
     def test_list_append_default_empty(self):
@@ -355,14 +355,14 @@ class TypeHintsTests(unittest.TestCase):
 
 
     def test_list_append_config(self):
-        parser = ArgumentParser(error_handler=None)
+        parser = ArgumentParser(exit_on_error=False)
         parser.add_argument('--cfg', action=ActionConfigFile)
         parser.add_argument('--val', type=List[int], default=[1, 2])
         self.assertEqual([3, 4], parser.parse_args(['--cfg', 'val: [3, 4]']).val)
         self.assertEqual([1, 2, 3], parser.parse_args(['--cfg', 'val+: 3']).val)
         self.assertEqual([1, 2, 3, 4], parser.parse_args(['--cfg', 'val+: [3, 4]']).val)
-        self.assertRaises(ParserError, lambda: parser.parse_args(['--cfg', 'val+: a']))
-        self.assertRaises(ParserError, lambda: parser.parse_args(['--val=2', '--cfg', 'val+: 3']))
+        self.assertRaises(ArgumentError, lambda: parser.parse_args(['--cfg', 'val+: a']))
+        self.assertRaises(ArgumentError, lambda: parser.parse_args(['--val=2', '--cfg', 'val+: 3']))
 
 
     def test_list_append_subclass_init_args(self):
@@ -370,7 +370,7 @@ class TypeHintsTests(unittest.TestCase):
             def __init__(self, p1: int = 0, p2: int = 0):
                 pass
 
-        parser = ArgumentParser(error_handler=None)
+        parser = ArgumentParser(exit_on_error=False)
         parser.add_argument('--val', type=Union[Class, List[Class]])
 
         with mock_module(Class) as module:
@@ -386,7 +386,7 @@ class TypeHintsTests(unittest.TestCase):
             def __init__(self, cal: Union[Calendar, Iterable[Calendar], bool] = True):
                 self.cal = cal
 
-        parser = ArgumentParser(error_handler=None)
+        parser = ArgumentParser(exit_on_error=False)
         parser.add_argument('cls', type=Class)
 
         cfg = parser.parse_args(['--cls.cal=calendar.TextCalendar', '--cls.cal.firstweekday=2'])
@@ -404,7 +404,7 @@ class TypeHintsTests(unittest.TestCase):
             def __init__(self, cals: Optional[Union[Calendar, List[Calendar]]] = None):
                 self.cals = cals
 
-        parser = ArgumentParser(error_handler=None)
+        parser = ArgumentParser(exit_on_error=False)
         parser.add_class_arguments(A, 'a')
         cfg = parser.parse_args([
             '--a.cals+=Calendar',
@@ -417,8 +417,8 @@ class TypeHintsTests(unittest.TestCase):
         cfg = parser.parse_args([f'--a={json.dumps(cfg.a.as_dict())}', '--a.cals.firstweekday=4'])
         self.assertEqual(Namespace(firstweekday=4), cfg.a.cals[-1].init_args)
         args = ['--a.cals+=Invalid', '--a.cals+=TextCalendar']
-        self.assertRaises(ParserError, lambda: parser.parse_args(args))
-        self.assertRaises(ParserError, lambda: parser.parse_args(args + ['--print_config']))
+        self.assertRaises(ArgumentError, lambda: parser.parse_args(args))
+        self.assertRaises(ArgumentError, lambda: parser.parse_args(args + ['--print_config']))
 
 
     def test_list_append_subcommand_subclass(self):
@@ -426,7 +426,7 @@ class TypeHintsTests(unittest.TestCase):
             def __init__(self, cals: Optional[Union[Calendar, List[Calendar]]] = None):
                 self.cals = cals
 
-        parser = ArgumentParser(error_handler=None)
+        parser = ArgumentParser(exit_on_error=False)
         subparser = ArgumentParser()
         subparser.add_class_arguments(A, 'a')
         subcommands = parser.add_subcommands()
@@ -443,19 +443,19 @@ class TypeHintsTests(unittest.TestCase):
         cfg = parser.parse_args(['cmd', f'--a={json.dumps(cfg.cmd.a.as_dict())}', '--a.cals.firstweekday=4'])
         self.assertEqual(Namespace(firstweekday=4), cfg.cmd.a.cals[-1].init_args)
         args = ['cmd', '--a.cals+=Invalid', '--a.cals+=TextCalendar']
-        self.assertRaises(ParserError, lambda: parser.parse_args(args))
-        self.assertRaises(ParserError, lambda: parser.parse_args(args + ['--print_config']))
+        self.assertRaises(ArgumentError, lambda: parser.parse_args(args))
+        self.assertRaises(ArgumentError, lambda: parser.parse_args(args + ['--print_config']))
 
 
     def test_restricted_number_type(self):
         limit_val = random.randint(100, 10000)
         larger_than = restricted_number_type(f'larger_than_{limit_val}', int, ('>', limit_val))
 
-        parser = ArgumentParser(error_handler=None)
+        parser = ArgumentParser(exit_on_error=False)
         parser.add_argument('--val', type=larger_than, default=limit_val+1, help='Description')
 
         self.assertEqual(limit_val+1, parser.parse_args([f'--val={limit_val+1}']).val)
-        self.assertRaises(ParserError, lambda: parser.parse_args([f'--val={limit_val-1}']))
+        self.assertRaises(ArgumentError, lambda: parser.parse_args([f'--val={limit_val-1}']))
 
         help_str = StringIO()
         parser.print_help(help_str)
@@ -463,7 +463,7 @@ class TypeHintsTests(unittest.TestCase):
 
 
     def test_type_Any(self):
-        parser = ArgumentParser(error_handler=None)
+        parser = ArgumentParser(exit_on_error=False)
         parser.add_argument('--any', type=Any)
         self.assertEqual('abc', parser.parse_args(['--any=abc'])['any'])
         self.assertEqual(123, parser.parse_args(['--any=123'])['any'])
@@ -484,7 +484,7 @@ class TypeHintsTests(unittest.TestCase):
                 self.cal1 = cal1
                 self.cal2 = cal2
 
-        parser = ArgumentParser(error_handler=None)
+        parser = ArgumentParser(exit_on_error=False)
         parser.add_argument('--any', type=Any)
 
         with mock_module(Class) as module:
@@ -527,7 +527,7 @@ class TypeHintsTests(unittest.TestCase):
 
 
     def test_type_any_list_of_subclasses(self):
-        parser = ArgumentParser(error_handler=None)
+        parser = ArgumentParser(exit_on_error=False)
         parser.add_argument('--any', type=Any)
 
         value = [
@@ -552,7 +552,7 @@ class TypeHintsTests(unittest.TestCase):
 
 
     def test_type_any_dict_of_subclasses(self):
-        parser = ArgumentParser(error_handler=None)
+        parser = ArgumentParser(exit_on_error=False)
         parser.add_argument('--any', type=Any)
 
         value = {
@@ -599,21 +599,21 @@ class TypeHintsTests(unittest.TestCase):
 
     @unittest.skipIf(not Literal, 'Literal introduced in python 3.8 or backported in typing_extensions')
     def test_Literal(self):
-        parser = ArgumentParser(error_handler=None)
+        parser = ArgumentParser(exit_on_error=False)
         parser.add_argument('--str', type=Literal['a', 'b', None])
         parser.add_argument('--int', type=Literal[3, 4])
         parser.add_argument('--true', type=Literal[True])
         parser.add_argument('--false', type=Literal[False])
         self.assertEqual('a', parser.parse_args(['--str=a']).str)
         self.assertEqual('b', parser.parse_args(['--str=b']).str)
-        self.assertRaises(ParserError, lambda: parser.parse_args(['--str=x']))
+        self.assertRaises(ArgumentError, lambda: parser.parse_args(['--str=x']))
         self.assertIsNone(parser.parse_args(['--str=null']).str)
         self.assertEqual(4, parser.parse_args(['--int=4']).int)
-        self.assertRaises(ParserError, lambda: parser.parse_args(['--int=5']))
+        self.assertRaises(ArgumentError, lambda: parser.parse_args(['--int=5']))
         self.assertIs(True, parser.parse_args(['--true=true']).true)
-        self.assertRaises(ParserError, lambda: parser.parse_args(['--true=false']))
+        self.assertRaises(ArgumentError, lambda: parser.parse_args(['--true=false']))
         self.assertIs(False, parser.parse_args(['--false=false']).false)
-        self.assertRaises(ParserError, lambda: parser.parse_args(['--false=true']))
+        self.assertRaises(ArgumentError, lambda: parser.parse_args(['--false=true']))
         out = StringIO()
         parser.print_help(out)
         for value in ['--str {a,b,null}', '--int {3,4}', '--true True', '--false False']:
@@ -628,7 +628,7 @@ class TypeHintsTests(unittest.TestCase):
 
 
     def _test_typehint_non_parameterized_types(self, type):
-        parser = ArgumentParser(error_handler=None)
+        parser = ArgumentParser(exit_on_error=False)
         ActionTypeHint.is_supported_typehint(type, full=True)
         parser.add_argument('--type', type=type)
         cfg = parser.parse_args(['--type=uuid.UUID'])
@@ -637,13 +637,13 @@ class TypeHintsTests(unittest.TestCase):
 
 
     def _test_typehint_parameterized_types(self, type):
-        parser = ArgumentParser(error_handler=None)
+        parser = ArgumentParser(exit_on_error=False)
         ActionTypeHint.is_supported_typehint(type, full=True)
         parser.add_argument('--cal', type=type[Calendar])
         cfg = parser.parse_args(['--cal=calendar.Calendar'])
         self.assertEqual(cfg.cal, Calendar)
         self.assertEqual(parser.dump(cfg), 'cal: calendar.Calendar\n')
-        self.assertRaises(ParserError, lambda: parser.parse_args(['--cal=uuid.UUID']))
+        self.assertRaises(ArgumentError, lambda: parser.parse_args(['--cal=uuid.UUID']))
 
 
     def test_typehint_Type(self):
@@ -663,7 +663,7 @@ class TypeHintsTests(unittest.TestCase):
     def test_uuid(self):
         id1 = uuid.uuid4()
         id2 = uuid.uuid4()
-        parser = ArgumentParser(error_handler=None)
+        parser = ArgumentParser(exit_on_error=False)
         parser.add_argument('--uuid', type=uuid.UUID)
         parser.add_argument('--uuids', type=List[uuid.UUID])
         cfg = parser.parse_args(['--uuid='+str(id1), '--uuids=["'+str(id1)+'", "'+str(id2)+'"]'])
@@ -674,16 +674,16 @@ class TypeHintsTests(unittest.TestCase):
 
     @unittest.skipIf(sys.version_info < (3, 10), 'new union syntax introduced in python 3.10')
     def test_union_new_syntax(self):
-        parser = ArgumentParser(error_handler=None)
+        parser = ArgumentParser(exit_on_error=False)
         parser.add_argument('--val', type=eval('int | None'))
         self.assertEqual(123, parser.parse_args(['--val=123']).val)
         self.assertIsNone(parser.parse_args(['--val=null']).val)
-        self.assertRaises(ParserError, lambda: parser.parse_args(['--val=abc']))
+        self.assertRaises(ArgumentError, lambda: parser.parse_args(['--val=abc']))
 
 
     @unittest.skipIf(sys.version_info < (3, 10), 'new union syntax introduced in python 3.10')
     def test_union_new_syntax_subclasses(self):
-        parser = ArgumentParser(error_handler=None)
+        parser = ArgumentParser(exit_on_error=False)
         parser.add_argument('--op', type=eval('Calendar | bool'))
         out = StringIO()
         with redirect_stdout(out), self.assertRaises(SystemExit):
@@ -692,7 +692,7 @@ class TypeHintsTests(unittest.TestCase):
 
 
     def test_Callable_with_function_path(self):
-        parser = ArgumentParser(error_handler=None)
+        parser = ArgumentParser(exit_on_error=False)
         parser.add_argument('--callable', type=Callable, default=time.time)
         parser.add_argument('--list', type=List[Callable])
 
@@ -706,7 +706,7 @@ class TypeHintsTests(unittest.TestCase):
         self.assertEqual(CLI, cfg.callable)
         self.assertEqual(parser.dump(cfg), 'callable: jsonargparse.CLI\n')
         self.assertEqual([CLI], parser.parse_args(['--list=[jsonargparse.CLI]']).list)
-        self.assertRaises(ParserError, lambda: parser.parse_args(['--callable=jsonargparse.not_exist']))
+        self.assertRaises(ArgumentError, lambda: parser.parse_args(['--callable=jsonargparse.not_exist']))
 
         out = StringIO()
         parser.print_help(out)
@@ -723,7 +723,7 @@ class TypeHintsTests(unittest.TestCase):
         class MyFunc2(MyFunc1):
             pass
 
-        parser = ArgumentParser(error_handler=None)
+        parser = ArgumentParser(exit_on_error=False)
         parser.add_argument('--callable', type=Callable)
 
         with mock_module(MyFunc1, MyFunc2) as module:
@@ -738,11 +738,11 @@ class TypeHintsTests(unittest.TestCase):
             self.assertIsInstance(cfg_init.callable, MyFunc1)
             self.assertEqual(cfg_init.callable(), 2)
 
-            self.assertRaises(ParserError, lambda: parser.parse_args(['--callable={}']))
-            self.assertRaises(ParserError, lambda: parser.parse_args(['--callable=jsonargparse.SUPPRESS']))
-            self.assertRaises(ParserError, lambda: parser.parse_args(['--callable=calendar.Calendar']))
+            self.assertRaises(ArgumentError, lambda: parser.parse_args(['--callable={}']))
+            self.assertRaises(ArgumentError, lambda: parser.parse_args(['--callable=jsonargparse.SUPPRESS']))
+            self.assertRaises(ArgumentError, lambda: parser.parse_args(['--callable=calendar.Calendar']))
             value = {'class_path': f'{module}.MyFunc1', 'key': 'val'}
-            self.assertRaises(ParserError, lambda: parser.parse_args([f'--callable={json.dumps(value)}']))
+            self.assertRaises(ArgumentError, lambda: parser.parse_args([f'--callable={json.dumps(value)}']))
 
 
     def test_callable_with_class_path_short_init_args(self):
@@ -788,7 +788,7 @@ class TypeHintsTests(unittest.TestCase):
         def my_func_2(p: str) -> int:
             return int(p)
 
-        parser = ArgumentParser(error_handler=None)
+        parser = ArgumentParser(exit_on_error=False)
         parser.add_argument('--callable', type=Callable[[int], str])
 
         with mock_module(my_func_1, my_func_2) as module:
@@ -820,7 +820,7 @@ class TypeHintsTests(unittest.TestCase):
         }
 
         with mock_module(Optimizer, SGD, Adam) as module:
-            parser = ArgumentParser(error_handler=None)
+            parser = ArgumentParser(exit_on_error=False)
             parser.add_argument('--optimizer', type=Callable[[List[float]], Optimizer], default=SGD)
 
             cfg = parser.get_defaults()
@@ -870,7 +870,7 @@ class TypeHintsTests(unittest.TestCase):
         }
 
         with mock_module(StepLR, ReduceLROnPlateau) as module:
-            parser = ArgumentParser(error_handler=None)
+            parser = ArgumentParser(exit_on_error=False)
             parser.add_argument('--scheduler', type=Callable[[Optimizer], Union[StepLR, ReduceLROnPlateau]], default=StepLR)
 
             cfg = parser.get_defaults()
@@ -896,7 +896,7 @@ class TypeHintsTests(unittest.TestCase):
 
 
     def test_class_type(self):
-        parser = ArgumentParser(error_handler=None)
+        parser = ArgumentParser(exit_on_error=False)
         parser.add_argument('--op', type=Optional[List[Calendar]])
 
         class_path = '"class_path": "calendar.Calendar"'
@@ -908,17 +908,17 @@ class TypeHintsTests(unittest.TestCase):
         cfg = parser.instantiate_classes(cfg)
         self.assertIsInstance(cfg['op'][0], Calendar)
 
-        with self.assertRaises(ParserError):
+        with self.assertRaises(ArgumentError):
             parser.parse_args(['--op=[{"class_path": "jsonargparse.ArgumentParser"}]'])
-        with self.assertRaises(ParserError):
+        with self.assertRaises(ArgumentError):
             parser.parse_args(['--op=[{"class_path": "jsonargparse.NotExist"}]'])
-        with self.assertRaises(ParserError):
+        with self.assertRaises(ArgumentError):
             parser.parse_args(['--op=[{"class_path": "jsonargparse0.IncorrectModule"}]'])
-        with self.assertRaises(ParserError):
+        with self.assertRaises(ArgumentError):
             parser.parse_args(['--op=[1]'])
 
         init_args = '"init_args": {"bad_arg": True}'
-        with self.assertRaises(ParserError):
+        with self.assertRaises(ArgumentError):
             parser.parse_args(['--op=[{'+class_path+', '+init_args+'}]'])
 
         init_args = '"init_args": {"firstweekday": 3}'
@@ -928,7 +928,7 @@ class TypeHintsTests(unittest.TestCase):
         self.assertIsInstance(cfg['op'][0], Calendar)
         self.assertEqual(3, cfg['op'][0].firstweekday)
 
-        parser = ArgumentParser(error_handler=None)
+        parser = ArgumentParser(exit_on_error=False)
         parser.add_argument('--n.op', type=Optional[Calendar])
         cfg = parser.parse_args(['--n.op={'+class_path+', '+init_args+'}'])
         cfg = parser.instantiate_classes(cfg)
@@ -951,7 +951,7 @@ class TypeHintsTests(unittest.TestCase):
             def __init__(self, p1: int = 1, p2: str = '2'):
                 pass
 
-        parser = ArgumentParser(error_handler=None)
+        parser = ArgumentParser(exit_on_error=False)
         parser.add_argument('--op', type=MyCal)
 
         with mock_module(MyCal) as module:
@@ -967,13 +967,13 @@ class TypeHintsTests(unittest.TestCase):
                 pass
 
         with mock_module(MyCal) as module:
-            parser = ArgumentParser(error_handler=None)
+            parser = ArgumentParser(exit_on_error=False)
             parser.add_argument('--op', type=MyCal, default=lazy_instance(MyCal))
 
             cfg = parser.get_defaults()
             self.assertEqual(cfg.op.class_path, f'{module}.MyCal')
             self.assertEqual(cfg.op.init_args, Namespace(p1=None, p2=None))
-            self.assertRaises(ParserError, lambda: parser.parse_args([f'--op={module}.MyCal']))
+            self.assertRaises(ArgumentError, lambda: parser.parse_args([f'--op={module}.MyCal']))
 
 
     def test_class_type_subclass_given_by_name_issue_84(self):
@@ -1039,7 +1039,7 @@ class TypeHintsTests(unittest.TestCase):
             def __init__(self, param_a: int = 1, param_b: str = 'x', **kwargs):
                 super().__init__(**kwargs)
 
-        parser = ArgumentParser(error_handler=None)
+        parser = ArgumentParser(exit_on_error=False)
         parser.add_argument('--cfg', action=ActionConfigFile)
         parser.add_argument('--cal', type=Calendar)
 
@@ -1196,7 +1196,7 @@ class TypeHintsTests(unittest.TestCase):
                 dict_kwargs={'p3': 7.0, 'p4': 'x'},
             )
 
-            parser = ArgumentParser(error_handler=None)
+            parser = ArgumentParser(exit_on_error=False)
             parser.add_argument('--config', action=ActionConfigFile)
             parser.add_argument('--cls', type=Class)
 
@@ -1209,7 +1209,7 @@ class TypeHintsTests(unittest.TestCase):
             cfg = parser.parse_args(['--cls=Class', '--cls.dict_kwargs.p4=-', '--cls.dict_kwargs.p3=7.0', '--cls.dict_kwargs.p4=x'])
             self.assertEqual(cfg.cls.dict_kwargs, expected.dict_kwargs)
 
-            with self.assertRaises(ParserError):
+            with self.assertRaises(ArgumentError):
                 parser.parse_args(['--cls=Class', '--cls.dict_kwargs=1'])
 
             out = StringIO()
@@ -1241,10 +1241,10 @@ class TypeHintsTests(unittest.TestCase):
             class_path: calendar.Calendar
             init_args:
         """
-        parser = ArgumentParser(error_handler=None)
+        parser = ArgumentParser(exit_on_error=False)
         parser.add_argument('--config', action=ActionConfigFile)
         parser.add_argument('--cal', type=Calendar)
-        self.assertRaises(ParserError, lambda: parser.parse_args([f'--config={config}']))
+        self.assertRaises(ArgumentError, lambda: parser.parse_args([f'--config={config}']))
 
 
     def test_help_known_subclasses_class(self):
@@ -1317,12 +1317,12 @@ class TypeHintsTests(unittest.TestCase):
 
 
     def test_nargs_questionmark(self):
-        parser = ArgumentParser(error_handler=None)
+        parser = ArgumentParser(exit_on_error=False)
         parser.add_argument('p1')
         parser.add_argument('p2', nargs='?', type=OpenUnitInterval)
         self.assertIsNone(parser.parse_args(['a']).p2)
         self.assertEqual(0.5, parser.parse_args(['a', '0.5']).p2)
-        self.assertRaises(ParserError, lambda: parser.parse_args(['a', 'b']))
+        self.assertRaises(ArgumentError, lambda: parser.parse_args(['a', 'b']))
 
 
     def test_register_type(self):
@@ -1334,7 +1334,7 @@ class TypeHintsTests(unittest.TestCase):
             return datetime.strptime(v, '%Y-%m-%dT%H:%M:%S')
 
         register_type(datetime, serializer, deserializer)
-        parser = ArgumentParser(error_handler=None)
+        parser = ArgumentParser(exit_on_error=False)
         parser.add_argument('--datetime', type=datetime)
         cfg = parser.parse_args(['--datetime=2008-09-03T20:56:35'])
         self.assertEqual(cfg.datetime, datetime(2008, 9, 3, 20, 56, 35))
@@ -1391,7 +1391,7 @@ class TypeHintsTmpdirTests(TempDirTestCase):
         with open(abs_yaml_file, 'w') as output_file:
             output_file.write('file: '+rel_yaml_file+'\ndir: '+self.tmpdir+'\n')
 
-        parser = ArgumentParser(error_handler=None)
+        parser = ArgumentParser(exit_on_error=False)
         parser.add_argument('--cfg', action=ActionConfigFile)
         parser.add_argument('--file', type=Path_fr)
         parser.add_argument('--dir', type=Path_drw)
@@ -1409,8 +1409,8 @@ class TypeHintsTmpdirTests(TempDirTestCase):
         cfg = parser.parse_args(['--file', abs_yaml_file, '--dir', self.tmpdir])
         self.assertEqual(self.tmpdir, os.path.realpath(cfg.dir()))
         self.assertEqual(abs_yaml_file, os.path.realpath(cfg.file()))
-        self.assertRaises(ParserError, lambda: parser.parse_args(['--dir', abs_yaml_file]))
-        self.assertRaises(ParserError, lambda: parser.parse_args(['--file', self.tmpdir]))
+        self.assertRaises(ArgumentError, lambda: parser.parse_args(['--dir', abs_yaml_file]))
+        self.assertRaises(ArgumentError, lambda: parser.parse_args(['--file', self.tmpdir]))
 
         cfg = parser.parse_args(['--files', abs_yaml_file, abs_yaml_file])
         self.assertTrue(isinstance(cfg.files, list))
@@ -1429,13 +1429,13 @@ class TypeHintsTmpdirTests(TempDirTestCase):
 
     def test_optional_path(self):
         pathlib.Path('file_fr').touch()
-        parser = ArgumentParser(error_handler=None)
+        parser = ArgumentParser(exit_on_error=False)
         parser.add_argument('--path', type=Optional[Path_fr])
         self.assertIsNone(parser.parse_args(['--path=null']).path)
         cfg = parser.parse_args(['--path=file_fr'])
         self.assertEqual('file_fr', cfg.path)
         self.assertIsInstance(cfg.path, Path)
-        self.assertRaises(ParserError, lambda: parser.parse_args(['--path=not_exist']))
+        self.assertRaises(ArgumentError, lambda: parser.parse_args(['--path=not_exist']))
 
 
     def test_enable_path(self):
@@ -1446,7 +1446,7 @@ class TypeHintsTmpdirTests(TempDirTestCase):
         with open('cal.yaml', 'w') as f:
             json.dump(cal, f)
 
-        parser = ArgumentParser(error_handler=None)
+        parser = ArgumentParser(exit_on_error=False)
         parser.add_argument('--data', type=Dict[str, Any], enable_path=True)
         parser.add_argument('--cal', type=Calendar, enable_path=True)
         cfg = parser.parse_args(['--data=data.yaml'])
@@ -1454,7 +1454,7 @@ class TypeHintsTmpdirTests(TempDirTestCase):
         self.assertEqual(data, cfg['data'])
         cfg = parser.instantiate_classes(parser.parse_args(['--cal=cal.yaml']))
         self.assertIsInstance(cfg['cal'], Calendar)
-        self.assertRaises(ParserError, lambda: parser.parse_args(['--data=does-not-exist.yaml']))
+        self.assertRaises(ArgumentError, lambda: parser.parse_args(['--data=does-not-exist.yaml']))
 
 
     def test_list_path_with_enable_path(self):
@@ -1477,7 +1477,7 @@ class TypeHintsTmpdirTests(TempDirTestCase):
         with open(list_file4, 'w') as output_file:
             output_file.write('file1\nfile2\nfile6\n')
 
-        parser = ArgumentParser(error_handler=None)
+        parser = ArgumentParser(exit_on_error=False)
         parser.add_argument(
             '--lists',
             nargs='+',
@@ -1495,7 +1495,7 @@ class TypeHintsTmpdirTests(TempDirTestCase):
         self.assertEqual(['file1', 'file2', 'file3', 'file4'], [str(x) for x in cfg.list])
 
         with unittest.mock.patch('sys.stdin', StringIO('file1\nfile2\n')):
-            with self.assertRaises(ParserError):
+            with self.assertRaises(ArgumentError):
                 parser.parse_args(['--list', '-'])
             with Path_drw('subdir').relative_path_context():
                 cfg = parser.parse_args(['--list', '-'])
@@ -1515,9 +1515,9 @@ class TypeHintsTmpdirTests(TempDirTestCase):
         cfg = parser.parse_args(['--lists', list_file3])
         self.assertEqual([[]], cfg.lists)
 
-        self.assertRaises(ParserError, lambda: parser.parse_args(['--lists']))
-        self.assertRaises(ParserError, lambda: parser.parse_args(['--lists', list_file4]))
-        self.assertRaises(ParserError, lambda: parser.parse_args(['--lists', 'no-such-file']))
+        self.assertRaises(ArgumentError, lambda: parser.parse_args(['--lists']))
+        self.assertRaises(ArgumentError, lambda: parser.parse_args(['--lists', list_file4]))
+        self.assertRaises(ArgumentError, lambda: parser.parse_args(['--lists', 'no-such-file']))
 
 
     def test_default_path_unregistered_type(self):
@@ -1623,7 +1623,7 @@ class TypeHintsTmpdirTests(TempDirTestCase):
             def __init__(self, cal: Optional[Calendar] = None, val: int = 2):
                 self.cal = cal
 
-        parser = ArgumentParser(error_handler=None, default_config_files=[config_path])
+        parser = ArgumentParser(exit_on_error=False, default_config_files=[config_path])
         parser.add_argument('--op', default='from default')
         parser.add_class_arguments(MyClass, 'data')
 
@@ -1683,7 +1683,7 @@ class TypeHintsTmpdirTests(TempDirTestCase):
                 json.dump({'cal': config}, f)
 
             parser = ArgumentParser(
-                error_handler=None,
+                exit_on_error=False,
                 logger={'level': 'DEBUG'},
                 default_config_files=[config_path],
             )
@@ -1714,7 +1714,7 @@ class TypeHintsTmpdirTests(TempDirTestCase):
                 self.int_list = int_list
 
         with mock_module(A, B) as module:
-            parser = ArgumentParser(error_handler=None)
+            parser = ArgumentParser(exit_on_error=False)
             parser.add_class_arguments(B, 'b')
 
             config = {
@@ -1736,7 +1736,7 @@ class TypeHintsTmpdirTests(TempDirTestCase):
             self.assertIsInstance(cfg_init.b.class_map['one'], A)
 
             config['b']['int_list'] = config['b']['class_map']
-            self.assertRaises(ParserError, lambda: parser.parse_object(config))
+            self.assertRaises(ArgumentError, lambda: parser.parse_object(config))
 
 
     def test_subcommand_with_subclass_default_override_lightning_issue_10859(self):
@@ -1753,7 +1753,7 @@ class TypeHintsTmpdirTests(TempDirTestCase):
             def __init__(self, a: int = 4, c: int = 5):
                 pass
 
-        parser = ArgumentParser(error_handler=None, logger={'level': 'DEBUG'})
+        parser = ArgumentParser(exit_on_error=False, logger={'level': 'DEBUG'})
         parser_subcommands = parser.add_subcommands()
         subparser = ArgumentParser()
         subparser.add_argument('--arch', type=Arch)
diff --git a/jsonargparse_tests/test_typing.py b/jsonargparse_tests/test_typing.py
index 514c4ce..19df1ed 100755
--- a/jsonargparse_tests/test_typing.py
+++ b/jsonargparse_tests/test_typing.py
@@ -6,7 +6,7 @@ import pickle
 import unittest
 from datetime import timedelta
 
-from jsonargparse import ArgumentParser, ParserError
+from jsonargparse import ArgumentError, ArgumentParser
 from jsonargparse.typing import (
     ClosedUnitInterval,
     Email,
@@ -110,7 +110,7 @@ class RestrictedNumberTests(unittest.TestCase):
         def gt0_or_off(x):
             return x if x == 'off' else PositiveInt(x)
 
-        parser = ArgumentParser(error_handler=None)
+        parser = ArgumentParser(exit_on_error=False)
         parser.add_argument('--le0', type=NonNegativeFloat)
         parser.add_argument('--f10t20', type=TenToTwenty, nargs='+')
         parser.add_argument('--gt0_or_off', type=gt0_or_off)
@@ -118,21 +118,21 @@ class RestrictedNumberTests(unittest.TestCase):
 
         self.assertEqual(0.0, parser.parse_args(['--le0', '0']).le0)
         self.assertEqual(5.6, parser.parse_args(['--le0', '5.6']).le0)
-        self.assertRaises(ParserError, lambda: parser.parse_args(['--le0', '-2.1']))
+        self.assertRaises(ArgumentError, lambda: parser.parse_args(['--le0', '-2.1']))
 
         self.assertEqual([11, 14, 16], parser.parse_args(['--f10t20', '11', '14', '16']).f10t20)
-        self.assertRaises(ParserError, lambda: parser.parse_args(['--f10t20', '9']))
-        self.assertRaises(ParserError, lambda: parser.parse_args(['--f10t20', '21']))
-        self.assertRaises(ParserError, lambda: parser.parse_args(['--f10t20', '10.5']))
+        self.assertRaises(ArgumentError, lambda: parser.parse_args(['--f10t20', '9']))
+        self.assertRaises(ArgumentError, lambda: parser.parse_args(['--f10t20', '21']))
+        self.assertRaises(ArgumentError, lambda: parser.parse_args(['--f10t20', '10.5']))
 
         self.assertEqual(1, parser.parse_args(['--gt0_or_off', '1']).gt0_or_off)
         self.assertEqual('off', parser.parse_args(['--gt0_or_off', 'off']).gt0_or_off)
-        self.assertRaises(ParserError, lambda: parser.parse_args(['--gt0_or_off', '0']))
-        self.assertRaises(ParserError, lambda: parser.parse_args(['--gt0_or_off', 'on']))
+        self.assertRaises(ArgumentError, lambda: parser.parse_args(['--gt0_or_off', '0']))
+        self.assertRaises(ArgumentError, lambda: parser.parse_args(['--gt0_or_off', 'on']))
 
         self.assertEqual([1, 'off'], parser.parse_args(['--multi_gt0_or_off', '1', 'off']).multi_gt0_or_off)
-        self.assertRaises(ParserError, lambda: parser.parse_args(['--multi_gt0_or_off', '1', '0']))
-        self.assertRaises(ParserError, lambda: parser.parse_object({'multi_gt0_or_off': [1, 0]}))
+        self.assertRaises(ArgumentError, lambda: parser.parse_args(['--multi_gt0_or_off', '1', '0']))
+        self.assertRaises(ArgumentError, lambda: parser.parse_object({'multi_gt0_or_off': [1, 0]}))
 
 
 class RestrictedStringTests(unittest.TestCase):
@@ -152,12 +152,12 @@ class RestrictedStringTests(unittest.TestCase):
 
     def test_add_argument_type(self):
         FourDigits = restricted_string_type('FourDigits', '^[0-9]{4}$')
-        parser = ArgumentParser(error_handler=None)
+        parser = ArgumentParser(exit_on_error=False)
         parser.add_argument('--op', type=FourDigits)
         self.assertEqual('1234', parser.parse_args(['--op', '1234']).op)
-        self.assertRaises(ParserError, lambda: parser.parse_args(['--op', '123']))
-        self.assertRaises(ParserError, lambda: parser.parse_args(['--op', '12345']))
-        self.assertRaises(ParserError, lambda: parser.parse_args(['--op', 'abcd']))
+        self.assertRaises(ArgumentError, lambda: parser.parse_args(['--op', '123']))
+        self.assertRaises(ArgumentError, lambda: parser.parse_args(['--op', '12345']))
+        self.assertRaises(ArgumentError, lambda: parser.parse_args(['--op', 'abcd']))
 
 
 class PathTypeTests(TempDirTestCase):
@@ -279,7 +279,7 @@ class OtherTests(unittest.TestCase):
         self.assertRaises(TypeError, lambda: not Elems(1, 2))
         register_type(Elems, lambda x: x.elems, lambda x: Elems(*x))
 
-        parser = ArgumentParser(error_handler=None)
+        parser = ArgumentParser(exit_on_error=False)
         parser.add_argument('--elems', type=Elems)
         cfg = parser.parse_args(['--elems=[1, 2, 3]'])
         self.assertIsInstance(cfg.elems, Elems)