diff --git a/onlinejudge_command/utils.py b/onlinejudge_command/utils.py
index de82639..02e867a 100644
--- a/onlinejudge_command/utils.py
+++ b/onlinejudge_command/utils.py
@@ -77,8 +77,13 @@ def exec_command(command_str: str, *, stdin: Optional[IO[Any]] = None, input: Op
         try:
             answer, _ = proc.communicate(input=input, timeout=timeout)
         except subprocess.TimeoutExpired:
+            pass
+        finally:
             if preexec_fn is not None:
-                os.killpg(os.getpgid(proc.pid), signal.SIGTERM)
+                try:
+                    os.killpg(os.getpgid(proc.pid), signal.SIGTERM)
+                except ProcessLookupError:
+                    pass
             else:
                 proc.terminate()
 

Zombie processes remains when terminate oj test with Ctrl-C
diff --git a/tests/command_test.py b/tests/command_test.py
index f5d7b25..d10aa46 100644
--- a/tests/command_test.py
+++ b/tests/command_test.py
@@ -3,7 +3,9 @@ import os
 import pathlib
 import random
 import shutil
+import signal
 import sys
+import threading
 import unittest
 
 import tests.utils
@@ -1011,7 +1013,7 @@ class TestTest(unittest.TestCase):
         )
 
     @unittest.skipIf(os.name == 'nt', "procfs is required")
-    def test_call_test_check_no_zombie(self):
+    def test_call_test_check_no_zombie_with_tle(self):
         marker = 'zombie-%08x' % random.randrange(2**32)
         data = self.snippet_call_test(
             args=['-c', tests.utils.python_c("import time; time.sleep(100)  # {}".format(marker)), '--tle', '1'],
@@ -1048,6 +1050,35 @@ class TestTest(unittest.TestCase):
             with open(str(cmdline), 'rb') as fh:
                 self.assertNotIn(marker.encode(), fh.read())
 
+    @unittest.skipIf(os.name == 'nt', "procfs is required")
+    def test_call_test_check_no_zombie_with_keyboard_interrupt(self):
+        marker_for_callee = 'zombie-%08x' % random.randrange(2**32)
+        marker_for_caller = 'zombie-%08x' % random.randrange(2**32)
+        files = [
+            {
+                'path': 'test/{}-1.in'.format(marker_for_caller),
+                'data': 'foo\n'
+            },
+        ]
+
+        def send_keyboard_interrupt():
+            for cmdline in pathlib.Path('/proc').glob('*/cmdline'):
+                with open(str(cmdline), 'rb') as fh:
+                    if marker_for_caller.encode() in fh.read():
+                        pid = int(cmdline.parts[2])
+                        print('sending Ctrl-C to', pid)
+                        os.kill(pid, signal.SIGINT)
+
+        timer = threading.Timer(1.0, send_keyboard_interrupt)
+        timer.start()
+        result = tests.utils.run_in_sandbox(args=['-v', 'test', '-c', tests.utils.python_c("import time; time.sleep(10)  # {}".format(marker_for_callee)), 'test/{}-1.in'.format(marker_for_caller)], files=files)
+        self.assertNotEqual(result['proc'].returncode, 0)
+
+        # check there are no processes whose command-line arguments contains the marker word
+        for cmdline in pathlib.Path('/proc').glob('*/cmdline'):
+            with open(str(cmdline), 'rb') as fh:
+                self.assertNotIn(marker_for_callee.encode(), fh.read())
+
 
 @unittest.skipIf(os.name == 'nt', "memory checking is disabled and output is different with Linux")
 class TestTestLog(unittest.TestCase):

pip install -e .[dev]
parse_log_pytest
pytest
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
appdirs==1.4.4
astroid==2.3.3
attrs==25.3.0
beautifulsoup4==4.13.3
certifi==2025.1.31
charset-normalizer==3.4.1
colorama==0.4.6
colorlog==6.9.0
coverage==7.8.0
diff-match-patch==20241021
exceptiongroup @ file:///croot/exceptiongroup_1706031385326/work
idna==3.10
iniconfig @ file:///home/linux1/recipes/ci/iniconfig_1610983019677/work
isort==4.3.21
jsonschema==4.23.0
jsonschema-specifications==2024.10.1
lazy-object-proxy==1.4.3
lxml==5.3.1
mccabe==0.6.1
mypy==0.770
mypy_extensions==0.4.4
online-judge-api-client==10.10.1
-e git+https://github.com/online-judge-tools/oj.git@99963a50f30b66ae6ddd6dde3e3fb235c62582e1#egg=online_judge_tools
packaging @ file:///croot/packaging_1734472117206/work
pluggy @ file:///croot/pluggy_1733169602837/work
pylint==2.4.4
pytest @ file:///croot/pytest_1738938843180/work
pytest-cov==6.0.0
referencing==0.36.2
requests==2.32.3
rpds-py==0.24.0
six==1.17.0
soupsieve==2.6
testfixtures==6.14.0
toml==0.10.2
tomli @ file:///opt/conda/conda-bld/tomli_1657175507142/work
typed-ast==1.4.3
typing_extensions==4.13.0
urllib3==2.3.0
wrapt==1.11.2
yapf==0.29.0

name: oj
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
  - exceptiongroup=1.2.0=py39h06a4308_0
  - iniconfig=1.1.1=pyhd3eb1b0_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
  - packaging=24.2=py39h06a4308_0
  - pip=25.0=py39h06a4308_0
  - pluggy=1.5.0=py39h06a4308_0
  - pytest=8.3.4=py39h06a4308_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
  - tomli=2.0.1=py39h06a4308_0
  - tzdata=2025a=h04d1e81_0
  - wheel=0.45.1=py39h06a4308_0
  - xz=5.6.4=h5eee18b_1
  - zlib=1.2.13=h5eee18b_1
  - pip:
      - appdirs==1.4.4
      - astroid==2.3.3
      - attrs==25.3.0
      - beautifulsoup4==4.13.3
      - certifi==2025.1.31
      - charset-normalizer==3.4.1
      - colorama==0.4.6
      - colorlog==6.9.0
      - coverage==7.8.0
      - diff-match-patch==20241021
      - idna==3.10
      - isort==4.3.21
      - jsonschema==4.23.0
      - jsonschema-specifications==2024.10.1
      - lazy-object-proxy==1.4.3
      - lxml==5.3.1
      - mccabe==0.6.1
      - mypy==0.770
      - mypy-extensions==0.4.4
      - online-judge-api-client==10.10.1
      - pylint==2.4.4
      - pytest-cov==6.0.0
      - referencing==0.36.2
      - requests==2.32.3
      - rpds-py==0.24.0
      - six==1.17.0
      - soupsieve==2.6
      - testfixtures==6.14.0
      - toml==0.10.2
      - typed-ast==1.4.3
      - typing-extensions==4.13.0
      - urllib3==2.3.0
      - wrapt==1.11.2
      - yapf==0.29.0
prefix: /opt/conda/envs/oj

tests/command_test.py::TestTest::test_call_test_check_no_zombie_with_keyboard_interrupt
tests/command_test.py::TestTest::test_call_test_large_memory tests/command_test.py::TestTest::test_call_test_memory_limit_error tests/command_test.py::TestTest::test_call_test_small_memory tests/command_test.py::TestTestLog::test_trailing_spaces
tests/command_test.py::TestTest::test_call_expected_uses_crlf tests/command_test.py::TestTest::test_call_non_unicode tests/command_test.py::TestTest::test_call_output_uses_both_lf_and_crlf tests/command_test.py::TestTest::test_call_output_uses_crlf tests/command_test.py::TestTest::test_call_runtime_error tests/command_test.py::TestTest::test_call_stderr tests/command_test.py::TestTest::test_call_stderr_and_fail tests/command_test.py::TestTest::test_call_test_check_no_zombie_with_tle tests/command_test.py::TestTest::test_call_test_dir tests/command_test.py::TestTest::test_call_test_float tests/command_test.py::TestTest::test_call_test_format tests/command_test.py::TestTest::test_call_test_format_hack tests/command_test.py::TestTest::test_call_test_format_select tests/command_test.py::TestTest::test_call_test_ignore_backup tests/command_test.py::TestTest::test_call_test_in_parallel tests/command_test.py::TestTest::test_call_test_multiline_all tests/command_test.py::TestTest::test_call_test_multiline_line tests/command_test.py::TestTest::test_call_test_norstrip tests/command_test.py::TestTest::test_call_test_not_tle tests/command_test.py::TestTest::test_call_test_re_glob_injection tests/command_test.py::TestTest::test_call_test_select tests/command_test.py::TestTest::test_call_test_shell tests/command_test.py::TestTest::test_call_test_simple tests/command_test.py::TestTest::test_call_test_special_judge tests/command_test.py::TestTest::test_call_test_tle
onlinejudge_command/utils.py
onlinejudge_command/utils.py