diff --git a/pre_commit/commands/run.py b/pre_commit/commands/run.py
index 95f8ab4..4f332ee 100644
--- a/pre_commit/commands/run.py
+++ b/pre_commit/commands/run.py
@@ -306,6 +306,15 @@ def run(
             f'`git add {config_file}` to fix this.',
         )
         return 1
+    if (
+            args.hook_stage in {'prepare-commit-msg', 'commit-msg'} and
+            not args.commit_msg_filename
+    ):
+        logger.error(
+            f'`--commit-msg-filename` is required for '
+            f'`--hook-stage {args.hook_stage}`',
+        )
+        return 1
 
     # Expose origin / source as environment variables for hooks to consume
     if args.origin and args.source:

TypeError: expected string or bytes-like object
OS: Ubuntu 19.10
App Version: pre-commit 2.1.0

Trying to create a commit-msg hook with the following hook yaml:

```yaml
---
- id: conform
  name: Conform
  description: Run 'conform enforce' for policy enforcement
  entry: conform enforce --commit-msg-file
  language: golang
  stages: [commit-msg]
```

However, when I run the `try-repo` command...

```bash
➜ pre-commit try-repo --hook-stage commit-msg .
[WARNING] Creating temporary repo with uncommitted changes...
[INFO] Initializing environment for /tmp/tmpinmhjsj2/shadow-repo.
===============================================================================
Using config:
===============================================================================
repos:
-   repo: /tmp/tmpinmhjsj2/shadow-repo
    rev: a4536d3f16eeddbe6faa39a19b3857b77967bd03
    hooks:
    -   id: conform
===============================================================================
[INFO] Installing environment for /tmp/tmpinmhjsj2/shadow-repo.
[INFO] Once installed this environment will be reused.
[INFO] This may take a few minutes...
An unexpected error has occurred: TypeError: expected string or bytes-like object
Check the log at /home/joelinux/.cache/pre-commit/pre-commit.log
```

The `pre-commit.log` file shows me this traceback:

```bash
Traceback (most recent call last):
  File "/usr/local/lib/python3.7/dist-packages/pre_commit/error_handler.py", line 54, in error_handler
    yield
  File "/usr/local/lib/python3.7/dist-packages/pre_commit/main.py", line 375, in main
    return try_repo(args)
  File "/usr/local/lib/python3.7/dist-packages/pre_commit/commands/try_repo.py", line 77, in try_repo
    return run(config_filename, store, args)
  File "/usr/local/lib/python3.7/dist-packages/pre_commit/commands/run.py", line 339, in run
    return _run_hooks(config, hooks, args, environ)
  File "/usr/local/lib/python3.7/dist-packages/pre_commit/commands/run.py", line 242, in _run_hooks
    _all_filenames(args), config['files'], config['exclude'],
  File "/usr/local/lib/python3.7/dist-packages/pre_commit/commands/run.py", line 62, in filter_by_include_exclude
    filename for filename in names
  File "/usr/local/lib/python3.7/dist-packages/pre_commit/commands/run.py", line 63, in <listcomp>
    if include_re.search(filename)
TypeError: expected string or bytes-like object
```

Looking through the code it looks like it's expecting to run the `filter_by_include_exclude()` method and it attempts to iterate over `names` which has a value of `(None,)`. Not sure if this is expected behavior but I wouldn't expect a commit-msg hook to filter any files since the only thing it should get is the filename of the commit message?
diff --git a/tests/commands/run_test.py b/tests/commands/run_test.py
index 87eef2e..4519ad1 100644
--- a/tests/commands/run_test.py
+++ b/tests/commands/run_test.py
@@ -663,12 +663,7 @@ def test_stages(cap_out, store, repo_with_passing_hook):
                 'language': 'pygrep',
                 'stages': [stage],
             }
-            for i, stage in enumerate(
-                (
-                    'commit', 'push', 'manual', 'prepare-commit-msg',
-                    'commit-msg',
-                ), 1,
-            )
+            for i, stage in enumerate(('commit', 'push', 'manual'), 1)
         ],
     }
     add_config_to_repo(repo_with_passing_hook, config)
@@ -686,8 +681,6 @@ def test_stages(cap_out, store, repo_with_passing_hook):
     assert _run_for_stage('commit').startswith(b'hook 1...')
     assert _run_for_stage('push').startswith(b'hook 2...')
     assert _run_for_stage('manual').startswith(b'hook 3...')
-    assert _run_for_stage('prepare-commit-msg').startswith(b'hook 4...')
-    assert _run_for_stage('commit-msg').startswith(b'hook 5...')
 
 
 def test_commit_msg_hook(cap_out, store, commit_msg_repo):
@@ -819,6 +812,16 @@ def test_error_with_unstaged_config(cap_out, store, modified_config_repo):
     assert ret == 1
 
 
+def test_commit_msg_missing_filename(cap_out, store, repo_with_passing_hook):
+    args = run_opts(hook_stage='commit-msg')
+    ret, printed = _do_run(cap_out, store, repo_with_passing_hook, args)
+    assert ret == 1
+    assert printed == (
+        b'[ERROR] `--commit-msg-filename` is required for '
+        b'`--hook-stage commit-msg`\n'
+    )
+
+
 @pytest.mark.parametrize(
     'opts', (run_opts(all_files=True), run_opts(files=[C.CONFIG_FILE])),
 )

pip install -e .[dev]
parse_log_pytest
requirements.txt
pytest
3.7
requirements-dev.txt
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
certifi @ file:///croot/certifi_1671487769961/work/certifi
cfgv==3.3.1
coverage==7.2.7
distlib==0.3.9
exceptiongroup==1.2.2
filelock==3.12.2
identify==2.5.24
importlib-metadata==6.7.0
iniconfig==2.0.0
nodeenv==1.9.1
packaging==24.0
platformdirs==4.0.0
pluggy==1.2.0
-e git+https://github.com/pre-commit/pre-commit.git@1c641b1c28ecc1005f46fdc76db4bbb0f67c82ac#egg=pre_commit
pytest==7.4.4
pytest-env==1.0.1
PyYAML==6.0.1
toml==0.10.2
tomli==2.0.1
typing_extensions==4.7.1
virtualenv==20.26.6
zipp==3.15.0

name: pre-commit
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
  - certifi=2022.12.7=py37h06a4308_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=1.1.1w=h7f8727e_0
  - pip=22.3.1=py37h06a4308_0
  - python=3.7.16=h7a1cb2a_0
  - readline=8.2=h5eee18b_0
  - setuptools=65.6.3=py37h06a4308_0
  - sqlite=3.45.3=h5eee18b_0
  - tk=8.6.14=h39e8969_0
  - wheel=0.38.4=py37h06a4308_0
  - xz=5.6.4=h5eee18b_1
  - zlib=1.2.13=h5eee18b_1
  - pip:
      - cfgv==3.3.1
      - coverage==7.2.7
      - distlib==0.3.9
      - exceptiongroup==1.2.2
      - filelock==3.12.2
      - identify==2.5.24
      - importlib-metadata==6.7.0
      - iniconfig==2.0.0
      - nodeenv==1.9.1
      - packaging==24.0
      - platformdirs==4.0.0
      - pluggy==1.2.0
      - pytest==7.4.4
      - pytest-env==1.0.1
      - pyyaml==6.0.1
      - toml==0.10.2
      - tomli==2.0.1
      - typing-extensions==4.7.1
      - virtualenv==20.26.6
      - zipp==3.15.0
prefix: /opt/conda/envs/pre-commit

tests/commands/run_test.py::test_commit_msg_missing_filename
tests/commands/run_test.py::test_start_msg tests/commands/run_test.py::test_full_msg tests/commands/run_test.py::test_full_msg_with_color tests/commands/run_test.py::test_full_msg_with_postfix tests/commands/run_test.py::test_full_msg_postfix_not_colored tests/commands/run_test.py::test_run_all_hooks_failing tests/commands/run_test.py::test_arbitrary_bytes_hook tests/commands/run_test.py::test_hook_that_modifies_but_returns_zero tests/commands/run_test.py::test_types_hook_repository tests/commands/run_test.py::test_exclude_types_hook_repository tests/commands/run_test.py::test_global_exclude tests/commands/run_test.py::test_global_files tests/commands/run_test.py::test_verbose_duration[1.234-2.0-\n- tests/commands/run_test.py::test_verbose_duration[1.0-1.0-\n- tests/commands/run_test.py::test_show_diff_on_failure[args0-All tests/commands/run_test.py::test_show_diff_on_failure[args1-All tests/commands/run_test.py::test_show_diff_on_failure[args2-reproduce tests/commands/run_test.py::test_run[options0-outputs0-0-True] tests/commands/run_test.py::test_run[options1-outputs1-0-True] tests/commands/run_test.py::test_run[options2-outputs2-0-True] tests/commands/run_test.py::test_run[options3-outputs3-1-True] tests/commands/run_test.py::test_run[options4-outputs4-1-True] tests/commands/run_test.py::test_run[options5-outputs5-0-True] tests/commands/run_test.py::test_run[options6-outputs6-0-True] tests/commands/run_test.py::test_run[options7-outputs7-0-False] tests/commands/run_test.py::test_run_output_logfile tests/commands/run_test.py::test_always_run tests/commands/run_test.py::test_always_run_alt_config tests/commands/run_test.py::test_hook_verbose_enabled tests/commands/run_test.py::test_origin_source_error_msg_error[master-] tests/commands/run_test.py::test_origin_source_error_msg_error[-master] tests/commands/run_test.py::test_all_push_options_ok tests/commands/run_test.py::test_has_unmerged_paths tests/commands/run_test.py::test_merge_conflict tests/commands/run_test.py::test_merge_conflict_modified tests/commands/run_test.py::test_merge_conflict_resolved tests/commands/run_test.py::test_compute_cols[hooks0-80] tests/commands/run_test.py::test_compute_cols[hooks1-81] tests/commands/run_test.py::test_compute_cols[hooks2-82] tests/commands/run_test.py::test_get_skips[environ0-expected_output0] tests/commands/run_test.py::test_get_skips[environ1-expected_output1] tests/commands/run_test.py::test_get_skips[environ2-expected_output2] tests/commands/run_test.py::test_get_skips[environ3-expected_output3] tests/commands/run_test.py::test_get_skips[environ4-expected_output4] tests/commands/run_test.py::test_get_skips[environ5-expected_output5] tests/commands/run_test.py::test_get_skips[environ6-expected_output6] tests/commands/run_test.py::test_skip_hook tests/commands/run_test.py::test_skip_aliased_hook tests/commands/run_test.py::test_hook_id_not_in_non_verbose_output tests/commands/run_test.py::test_hook_id_in_verbose_output tests/commands/run_test.py::test_multiple_hooks_same_id tests/commands/run_test.py::test_aliased_hook_run tests/commands/run_test.py::test_non_ascii_hook_id tests/commands/run_test.py::test_stdout_write_bug_py26 tests/commands/run_test.py::test_lots_of_files tests/commands/run_test.py::test_stages tests/commands/run_test.py::test_commit_msg_hook tests/commands/run_test.py::test_prepare_commit_msg_hook tests/commands/run_test.py::test_local_hook_passes tests/commands/run_test.py::test_local_hook_fails tests/commands/run_test.py::test_meta_hook_passes tests/commands/run_test.py::test_error_with_unstaged_config tests/commands/run_test.py::test_no_unstaged_error_with_all_files_or_files[opts0] tests/commands/run_test.py::test_no_unstaged_error_with_all_files_or_files[opts1] tests/commands/run_test.py::test_files_running_subdir tests/commands/run_test.py::test_pass_filenames[True-hook_args0-foo.py] tests/commands/run_test.py::test_pass_filenames[False-hook_args1-] tests/commands/run_test.py::test_pass_filenames[True-hook_args2-some tests/commands/run_test.py::test_pass_filenames[False-hook_args3-some tests/commands/run_test.py::test_fail_fast tests/commands/run_test.py::test_classifier_removes_dne tests/commands/run_test.py::test_classifier_normalizes_filenames_on_windows_to_forward_slashes tests/commands/run_test.py::test_classifier_does_not_normalize_backslashes_non_windows tests/commands/run_test.py::test_include_exclude_base_case tests/commands/run_test.py::test_matches_broken_symlink tests/commands/run_test.py::test_include_exclude_total_match tests/commands/run_test.py::test_include_exclude_does_search_instead_of_match tests/commands/run_test.py::test_include_exclude_exclude_removes_files tests/commands/run_test.py::test_args_hook_only
pre_commit/commands/run.py
pre_commit/commands/run.py