diff --git a/CHANGELOG.md b/CHANGELOG.md
index 37999bbf..57b321fc 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,7 @@
 ## 0.9.16 (TBD, 2019)
 * Enhancements
     * Raise `TypeError` if trying to set choices/completions on argparse action that accepts no arguments
+    * Create directory for the persistent history file if it does not already exist
 
 ## 0.9.15 (July 24, 2019)
 * Bug Fixes
diff --git a/cmd2/cmd2.py b/cmd2/cmd2.py
index adadbdf8..7c529acc 100644
--- a/cmd2/cmd2.py
+++ b/cmd2/cmd2.py
@@ -3603,15 +3603,25 @@ class Cmd(cmd.Cmd):
 
         hist_file = os.path.abspath(os.path.expanduser(hist_file))
 
-        # first we try and unpickle the history file
-        history = History()
         # on Windows, trying to open a directory throws a permission
         # error, not a `IsADirectoryError`. So we'll check it ourselves.
         if os.path.isdir(hist_file):
-            msg = "persistent history file '{}' is a directory"
+            msg = "Persistent history file '{}' is a directory"
             self.perror(msg.format(hist_file))
             return
 
+        # Create the directory for the history file if it doesn't already exist
+        hist_file_dir = os.path.dirname(hist_file)
+        try:
+            os.makedirs(hist_file_dir, exist_ok=True)
+        except OSError as ex:
+            msg = "Error creating persistent history file directory '{}': {}".format(hist_file_dir, ex)
+            self.pexcept(msg)
+            return
+
+        # first we try and unpickle the history file
+        history = History()
+
         try:
             with open(hist_file, 'rb') as fobj:
                 history = pickle.load(fobj)
@@ -3619,7 +3629,7 @@ class Cmd(cmd.Cmd):
             # If any non-operating system error occurs when attempting to unpickle, just use an empty history
             pass
         except OSError as ex:
-            msg = "can not read persistent history file '{}': {}"
+            msg = "Can not read persistent history file '{}': {}"
             self.pexcept(msg.format(hist_file, ex))
             return
 
@@ -3655,7 +3665,7 @@ class Cmd(cmd.Cmd):
             with open(self.persistent_history_file, 'wb') as fobj:
                 pickle.dump(self.history, fobj)
         except OSError as ex:
-            msg = "can not write persistent history file '{}': {}"
+            msg = "Can not write persistent history file '{}': {}"
             self.pexcept(msg.format(self.persistent_history_file, ex))
 
     def _generate_transcript(self, history: List[Union[HistoryItem, str]], transcript_file: str) -> None:

persistent history file should create directory if needed
When specifying a persistent history file, it would be nice for it to create the parent directories for you. For example, if you have a suite of tools that will store configuration and history in a common directory in the user home directory. 

For example, have a directory `/home/username/.myapp/` be created automatically if persistent history file is set to `/home/username/.myapp/myhistory`.
diff --git a/tests/test_history.py b/tests/test_history.py
index 2da34cfd..9752ed07 100644
--- a/tests/test_history.py
+++ b/tests/test_history.py
@@ -638,6 +638,36 @@ def test_history_file_is_directory(capsys):
         _, err = capsys.readouterr()
         assert 'is a directory' in err
 
+def test_history_can_create_directory(mocker):
+    # Mock out atexit.register so the persistent file doesn't written when this function
+    # exists because we will be deleting the directory it needs to go to.
+    mock_register = mocker.patch('atexit.register')
+
+    # Create a temp path for us to use and let it get deleted
+    with tempfile.TemporaryDirectory() as test_dir:
+        pass
+    assert not os.path.isdir(test_dir)
+
+    # Add some subdirectories for the complete history file directory
+    hist_file_dir = os.path.join(test_dir, 'subdir1', 'subdir2')
+    hist_file = os.path.join(hist_file_dir, 'hist_file')
+
+    # Make sure cmd2 creates the history file directory
+    cmd2.Cmd(persistent_history_file=hist_file)
+    assert os.path.isdir(hist_file_dir)
+
+    # Cleanup
+    os.rmdir(hist_file_dir)
+
+def test_history_cannot_create_directory(mocker, capsys):
+    mock_open = mocker.patch('os.makedirs')
+    mock_open.side_effect = OSError
+
+    hist_file_path = os.path.join('fake_dir', 'file')
+    cmd2.Cmd(persistent_history_file=hist_file_path)
+    _, err = capsys.readouterr()
+    assert 'Error creating persistent history file directory' in err
+
 def test_history_file_permission_error(mocker, capsys):
     mock_open = mocker.patch('builtins.open')
     mock_open.side_effect = PermissionError
@@ -645,7 +675,7 @@ def test_history_file_permission_error(mocker, capsys):
     cmd2.Cmd(persistent_history_file='/tmp/doesntmatter')
     out, err = capsys.readouterr()
     assert not out
-    assert 'can not read' in err
+    assert 'Can not read' in err
 
 def test_history_file_conversion_no_truncate_on_init(hist_file, capsys):
     # make sure we don't truncate the plain text history file on init
@@ -720,4 +750,4 @@ def test_persist_history_permission_error(hist_file, mocker, capsys):
     app._persist_history()
     out, err = capsys.readouterr()
     assert not out
-    assert 'can not write' in err
+    assert 'Can not write' in err

pip install -e .[dev]
parse_log_pytest
Pipfile
pytest pytest-cov pytest-mock pytest
apt-get update apt-get install -y gcc
3.9
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
alabaster==0.7.16
anyio==4.9.0
attrs==25.3.0
babel==2.17.0
backports.tarfile==1.2.0
cachetools==5.5.2
certifi==2025.1.31
cffi==1.17.1
chardet==5.2.0
charset-normalizer==3.4.1
click==8.1.8
-e git+https://github.com/python-cmd2/cmd2.git@a5e52820df8da804347ef5a59519758f547bcb0f#egg=cmd2
codecov==2.1.13
colorama==0.4.6
coverage==7.8.0
cryptography==44.0.2
distlib==0.3.9
doc8==1.1.2
docutils==0.21.2
exceptiongroup==1.2.2
filelock==3.18.0
flake8==7.2.0
h11==0.14.0
id==1.5.0
idna==3.10
imagesize==1.4.1
importlib_metadata==8.6.1
iniconfig==2.1.0
invoke==2.2.0
jaraco.classes==3.4.0
jaraco.context==6.0.1
jaraco.functools==4.1.0
jeepney==0.9.0
Jinja2==3.1.6
keyring==25.6.0
markdown-it-py==3.0.0
MarkupSafe==3.0.2
mccabe==0.7.0
mdurl==0.1.2
more-itertools==10.6.0
nh3==0.2.21
packaging==24.2
pbr==6.1.1
pipfile==0.0.2
platformdirs==4.3.7
pluggy==1.5.0
pycodestyle==2.13.0
pycparser==2.22
pyflakes==3.3.1
Pygments==2.19.1
pyperclip==1.9.0
pyproject-api==1.9.0
pytest==8.3.5
pytest-cov==6.0.0
pytest-mock==3.14.0
readme_renderer==44.0
requests==2.32.3
requests-toolbelt==1.0.0
restructuredtext_lint==1.4.0
rfc3986==2.0.0
rich==14.0.0
SecretStorage==3.3.3
sniffio==1.3.1
snowballstemmer==2.2.0
Sphinx==7.4.7
sphinx-autobuild==2024.10.3
sphinx-rtd-theme==3.0.2
sphinxcontrib-applehelp==2.0.0
sphinxcontrib-devhelp==2.0.0
sphinxcontrib-htmlhelp==2.1.0
sphinxcontrib-jquery==4.1
sphinxcontrib-jsmath==1.0.1
sphinxcontrib-qthelp==2.0.0
sphinxcontrib-serializinghtml==2.0.0
starlette==0.46.1
stevedore==5.4.1
toml @ file:///tmp/build/80754af9/toml_1616166611790/work
tomli==2.2.1
tox==4.25.0
twine==6.1.0
typing_extensions==4.13.0
urllib3==2.3.0
uvicorn==0.34.0
virtualenv==20.29.3
watchfiles==1.0.4
wcwidth==0.2.13
websockets==15.0.1
zipp==3.21.0

name: cmd2
channels:
  - defaults
  - https://repo.anaconda.com/pkgs/main
  - https://repo.anaconda.com/pkgs/r
  - conda-forge
dependencies:
  - _libgcc_mutex=0.1=main
  - _openmp_mutex=5.1=1_gnu
  - ca-certificates=2025.2.25=h06a4308_0
  - ld_impl_linux-64=2.40=h12ee557_0
  - libffi=3.4.4=h6a678d5_1
  - libgcc-ng=11.2.0=h1234567_1
  - libgomp=11.2.0=h1234567_1
  - libstdcxx-ng=11.2.0=h1234567_1
  - ncurses=6.4=h6a678d5_0
  - openssl=3.0.16=h5eee18b_0
  - pip=25.0=py39h06a4308_0
  - pipfile=0.0.2=py_0
  - python=3.9.21=he870216_1
  - readline=8.2=h5eee18b_0
  - setuptools=75.8.0=py39h06a4308_0
  - sqlite=3.45.3=h5eee18b_0
  - tk=8.6.14=h39e8969_0
  - toml=0.10.2=pyhd3eb1b0_0
  - tzdata=2025a=h04d1e81_0
  - wheel=0.45.1=py39h06a4308_0
  - xz=5.6.4=h5eee18b_1
  - zlib=1.2.13=h5eee18b_1
  - pip:
      - alabaster==0.7.16
      - anyio==4.9.0
      - attrs==25.3.0
      - babel==2.17.0
      - backports-tarfile==1.2.0
      - cachetools==5.5.2
      - certifi==2025.1.31
      - cffi==1.17.1
      - chardet==5.2.0
      - charset-normalizer==3.4.1
      - click==8.1.8
      - codecov==2.1.13
      - colorama==0.4.6
      - coverage==7.8.0
      - cryptography==44.0.2
      - distlib==0.3.9
      - doc8==1.1.2
      - docutils==0.21.2
      - exceptiongroup==1.2.2
      - filelock==3.18.0
      - flake8==7.2.0
      - h11==0.14.0
      - id==1.5.0
      - idna==3.10
      - imagesize==1.4.1
      - importlib-metadata==8.6.1
      - iniconfig==2.1.0
      - invoke==2.2.0
      - jaraco-classes==3.4.0
      - jaraco-context==6.0.1
      - jaraco-functools==4.1.0
      - jeepney==0.9.0
      - jinja2==3.1.6
      - keyring==25.6.0
      - markdown-it-py==3.0.0
      - markupsafe==3.0.2
      - mccabe==0.7.0
      - mdurl==0.1.2
      - more-itertools==10.6.0
      - nh3==0.2.21
      - packaging==24.2
      - pbr==6.1.1
      - platformdirs==4.3.7
      - pluggy==1.5.0
      - pycodestyle==2.13.0
      - pycparser==2.22
      - pyflakes==3.3.1
      - pygments==2.19.1
      - pyperclip==1.9.0
      - pyproject-api==1.9.0
      - pytest==8.3.5
      - pytest-cov==6.0.0
      - pytest-mock==3.14.0
      - readme-renderer==44.0
      - requests==2.32.3
      - requests-toolbelt==1.0.0
      - restructuredtext-lint==1.4.0
      - rfc3986==2.0.0
      - rich==14.0.0
      - secretstorage==3.3.3
      - sniffio==1.3.1
      - snowballstemmer==2.2.0
      - sphinx==7.4.7
      - sphinx-autobuild==2024.10.3
      - sphinx-rtd-theme==3.0.2
      - sphinxcontrib-applehelp==2.0.0
      - sphinxcontrib-devhelp==2.0.0
      - sphinxcontrib-htmlhelp==2.1.0
      - sphinxcontrib-jquery==4.1
      - sphinxcontrib-jsmath==1.0.1
      - sphinxcontrib-qthelp==2.0.0
      - sphinxcontrib-serializinghtml==2.0.0
      - starlette==0.46.1
      - stevedore==5.4.1
      - tomli==2.2.1
      - tox==4.25.0
      - twine==6.1.0
      - typing-extensions==4.13.0
      - urllib3==2.3.0
      - uvicorn==0.34.0
      - virtualenv==20.29.3
      - watchfiles==1.0.4
      - wcwidth==0.2.13
      - websockets==15.0.1
      - zipp==3.21.0
prefix: /opt/conda/envs/cmd2

tests/test_history.py::test_history_can_create_directory tests/test_history.py::test_history_cannot_create_directory tests/test_history.py::test_history_file_permission_error tests/test_history.py::test_persist_history_permission_error
tests/test_history.py::test_readline_remove_history_item tests/test_history.py::test_history_class_span tests/test_history.py::test_persisted_history_span tests/test_history.py::test_history_class_get tests/test_history.py::test_history_str_search tests/test_history.py::test_history_regex_search tests/test_history.py::test_history_max_length_zero tests/test_history.py::test_history_max_length_negative tests/test_history.py::test_history_max_length tests/test_history.py::test_multiline_histitem tests/test_history.py::test_multiline_histitem_verbose tests/test_history.py::test_history_item_instantiate tests/test_history.py::test_history_item_properties tests/test_history.py::test_base_history tests/test_history.py::test_history_script_format tests/test_history.py::test_history_with_string_argument tests/test_history.py::test_history_expanded_with_string_argument tests/test_history.py::test_history_expanded_with_regex_argument tests/test_history.py::test_history_with_integer_argument tests/test_history.py::test_history_with_integer_span tests/test_history.py::test_history_with_span_start tests/test_history.py::test_history_with_span_end tests/test_history.py::test_history_with_span_index_error tests/test_history.py::test_history_output_file tests/test_history.py::test_history_bad_output_file tests/test_history.py::test_history_edit tests/test_history.py::test_history_run_all_commands tests/test_history.py::test_history_run_one_command tests/test_history.py::test_history_clear tests/test_history.py::test_history_verbose_with_other_options tests/test_history.py::test_history_verbose tests/test_history.py::test_history_script_with_invalid_options tests/test_history.py::test_history_script tests/test_history.py::test_history_expanded_with_invalid_options tests/test_history.py::test_history_expanded tests/test_history.py::test_history_script_expanded tests/test_history.py::test_base_help_history tests/test_history.py::test_exclude_from_history tests/test_history.py::test_history_file_is_directory tests/test_history.py::test_history_file_conversion_no_truncate_on_init tests/test_history.py::test_history_populates_readline tests/test_history.py::test_persist_history_ensure_no_error_if_no_histfile
cmd2/cmd2.py CHANGELOG.md
cmd2/cmd2.py CHANGELOG.md