About This Article
This article is generated using an automated generation workflow leveraging generative AI. Based on the official Python 3.13subprocessspecifications, initial testing was performed on Python 3.13.5 under Linux, and sample code with a fixed commit was re-verified on Python 3.12.14 on October 28, 2026. Reusable code and unit tests are separated into Daily-Code-Samples.Verification Status: 🧪 Confirmed 3 CLI branches and 9 unit tests on Linux / Python 3.13.5 and 3.12.14. Windows, macOS, and production operational commands are unverified.
When calling external commands from Python, do you assume success just because no errors are displayed on the screen? In reality,failed to start, started but terminated abnormally, or stopped due to a timeoutare three distinct states. Failure to distinguish these will lead subsequent batch processing steps to mistakenly assume that the preceding process has completed, resulting in sending stale data or adopting incomplete results.
This time, without touching external services or actual files, we will launch Python itself as a child process. We will intentionally trigger normal termination, abnormal termination, and waiting,subprocess.run()and observe the return values and exceptions. Finally, we will organize the criteria for incorporating this into operational scripts.
- First, Comparing the Three Types of Results
- Observing exit codes and output destinations with minimal code
- "Failure" and "Timeout" are not the same thing.
- Changing one place: trying to turn a timeout into a normal termination
- Troubleshooting failures: handling cases where the process cannot be started separately
- When using this in a professional environment, how far should safety be ensured?
- Verification results and remaining scope
- Conclusion
- Official Information / Primary Sources
- GitHub Sample
First, Comparing the Three Types of Results
The prerequisite is Python 3.9 or later. No additional packages or administrator privileges are required. The experiment used Python 3.13.5 on Linux, and these publication procedures have not yet been physically verified on Windows and macOS.
The completed version of therun_demo.pyscript can be obtained from theGitHub samples, and executedone by onein the folder containing the file.
python3 run_demo.py --mode ok python3 run_demo.py --mode fail python3 run_demo.py --mode timeout --timeout 5
failandtimeoutintentionally returns a non-zero exit code.&&Since chaining them with might prevent the next experiment from running halfway through, try them as independent commands.
The actual measurement results for normal termination are as follows.
STATUS=OK CHILD_RETURN_CODE=0 STDOUT=child: done STDERR=
In abnormal termination, the child process itself returned an exit code of 7 and output characters to the error destination.
STATUS=NONZERO CHILD_RETURN_CODE=7 STDOUT= STDERR=child: failed
In the case of a timeout, the waiting child process was terminated. In this Linux actual measurement, the line indicating the start of waiting was also successfully acquired.
STATUS=TIMEOUT CHILD_RETURN_CODE=N/A STDOUT=child: waiting STDERR=
What is important here is thatthe exit code of the child process and the exit code of this test program itself are separatethings.fail's child process exited with 7, but the test program returns 1 in the sense that "abnormal termination was detected."timeoutSince the normal exit code of the child process cannot be obtained with ,N/Ais displayed, and the test program returns 124. 124 and 127 are classification values adopted by this learning material and do not have a unified meaning across Python as a whole.
Observing exit codes and output destinations with minimal code
First, let's temporarily remove the timeout handling from consideration. A minimal example that invokes Python itself and outputs a single line to standard output is as follows.
import subprocess
import sys
result = subprocess.run(
[sys.executable, "-c", "print('hello')"],
capture_output=True,
text=True,
encoding="utf-8",
check=False,
shell=False,
)
print(result.returncode)
print(result.stdout.strip())
print(result.stderr.strip())
The first thing to pay attention to in this code is[sys.executable, "-c", ...]thelist of argumentsnamed .sys.executablerefers to the executable file of the currently running Python interpreter. This is a technique to use the same Python without guessing whetherpythonorpython3will work depending on the OS. The official explanation isPlease refer to Python sys.executable.Please refer to this.
capture_output=TrueBy specifying this, you can capture the standard output and standard error of the child process separately.stdoutis used for normal results, andstderris often used for errors and diagnostic messages. However,you should not assume a failure simply because there is text in stderr. Some normal tools output progress to stderr. Use to make the basic determination of success or failure, and read the messages as material for investigating the cause.returncodeUse this to make the basic determination of success or failure, and read the messages to investigate the cause.
check=FalseIn this case, even with a non-zero exit code,CompletedProcessis returned. Conversely, by specifyingcheck=True, an exception is raised in case of a non-zero value.CalledProcessErrorIt is not a matter of which one is correct, but rather how the caller wants to handle abnormal termination. Since we want to observe the states side by side this time, we have chosencheck=False.The official Python subprocess.run specificationalso explains these arguments and return values.
"Failure" and "Timeout" are not the same thing.
For example, suppose a nightly batch job calls an external program that generates a CSV. If the child process returns an exit code of 7,you know that the program started and proceeded to the point of returning an exit status. On the other hand, in the case of a timeout, you know that completion could not be confirmed within the specified time. You must separately verify whether the file has been completed.
Here is an example that simplifies timeout handling.
import subprocess
import sys
try:
result = subprocess.run(
[sys.executable, "-c", "import time; time.sleep(30)"],
timeout=5,
capture_output=True,
text=True,
check=False,
)
print("returncode:", result.returncode)
except subprocess.TimeoutExpired:
print("TIMEOUT: 完了を確認できませんでした")
timeout=5is not a guarantee that it will return in exactly 5 seconds from start under all conditions. The official Python documentation explains that depending on the environment, the process creation itself cannot be interrupted midway, and that when the deadline is reached, it terminates the child process and raises an exception after waiting. If strict processing time limits are required, OS-level monitoring and process management must also be part of the design scope.
Also, upon timeout,TimeoutExpired.stdoutandstderrmay contain output obtained up to that point. Thispartial output can betext=Truea byte sequence even if specified.In the final version, we provide logic to decode as UTF-8 if it is bytes, or convert to an empty string if it is None. This prevents crashes during display processing caused by unexpected exceptions only when a timeout occurs.
Changing one place: trying to turn a timeout into a normal termination
The next experiment is just changing the 5 in--mode timeout --timeout 5to 40.
python3 run_demo.py --mode timeout --timeout 40
The child process in this mode waits for 30 seconds before exiting. Therefore, if we allocate a sufficient margin of 40 seconds, we can normally expect it to becomeSTATUS=OK.Re-running with the 40-second setting has not been performed this time, and this is an expectation based on the specifications and the code. Since it waits for 30 seconds, you do not need to run it if you only want to perform short tests.
From this comparison, we can see that a timeout is not merely another name for a "failure," but rathera state determined by the relationship between the allotted wait time and the time required for processing. In practical applications, it is necessary to decide in advance how many seconds to wait when processing is slow, whether the process can be safely stopped midway, and what to check after it has been stopped.
Troubleshooting failures: handling cases where the process cannot be started separately
subprocess.run()Even if you call, if the executable file cannot be found, you cannot even obtain the exit code of the child process. Examples of such cases include missing PATH configurations, incorrect executable file names, or a lack of execution permissions.
of the final versionrun_checked()andOSErroris received and returned asSTART_FAILED. The educational materials do not display actual paths or full commands, but onlyFileNotFoundErrorexception types such asonly.This is to prevent environment information and secrets from leaking into the logs. When investigating the cause, verify the executing user, the existence of the executable file, permissions, and the working directory separately in a secure location.
On the other hand, if it isNONZERO, the child process has been started. Check the standard error output or the program-specific exit code table.TIMEOUTIf it is
| , check whether the processing has stopped, is simply slow, or has external resources waiting. Even for the same "processing failure," the starting point for investigation differs. | State | First thing to check |
|---|---|---|
| What not to do casually | OK | Validity and count of output data |
| Concluding business success solely based on an exit code of 0 | NONZERO | Child exit code and stderr |
| Unconditional automatic re-execution | TIMEOUT | Treating intermediate files as finished products |
| START_FAILED | Executable files, permissions, and PATH | Exposure of full exception traces or secret values |
When using this in a professional environment, how far should safety be ensured?
In this tutorial materialshell=Falseand use an argument list. By executing without having the shell interpret a single string, the handling of quotes and special characters can be simplified. However,shell=Falsedoes not automatically make any external command secure on its own. A design that allows unrestricted selection of executable names or arguments from external inputs leaves other risks. In practical operations, use only permitted commands and arguments, and execute them with the minimum necessary privileges.
Furthermore,capture_output=Trueretains results in memory. It is not suitable for processes that output large amounts of logs, and consideration should be given to using an output file or sequential reading. Because logs may contain tokens, personal information, environment variables, and other sensitive data, it is also necessary to limit the items collected.
When a child process spawns another grandchild process,subprocess.run()a timeout alone does not guarantee the termination of the entire process group. In critical batch jobs, process tree termination management, exclusive control, idempotency upon re-execution, and intermediate file inspection should be treated as separate design items.
Also, an exit code of 0 merely indicates that "the program determined it terminated normally." Separate verification is required to confirm whether the output CSV has the correct number of columns, whether the expected record count is present, and whether the subsequent system has accepted it. What is important in automation isto separate the evidence that the program ran from the evidence that the business result is correct.
Verification results and remaining scope
The completed version of Daily-Code-Samples provides 9 unit tests covering normal termination, non-zero termination, timeout, startup failure, partial output byte sequences, argument lists andshell=False, and invalid timeout values. All 9 tests succeeded on Linux / Python 3.13.5, andpy_compilealso succeeded. The CLI normal, non-zero, and 5-second timeout cases were also executed individually,STATUS=OK、NONZERO、TIMEOUTconfirmed.
In the re-verification on October 28, 2026, the same pinned commit was read back on Linux / Python 3.12.14,py_compile, running 9 unit tests, successful CLI termination, exit code 7, and a 1-second timeout. The results were respectivelySTATUS=OK/ caller exit code 1 /STATUS=TIMEOUTand caller exit code 124, which matched the classification in the main text. The 1-second duration is a condition changed to shorten the re-verification time and is distinguished from the 5-second example in the text and the unperformed 40-second experiment.
However, startup failures and timeouts within unit tests were confirmed using mocks; actual device testing on Windows and macOS or connection testing with production programs was not performed. Do not conflate these scopes to claim that it has been "verified in all environments."
Conclusion
subprocess.run()If usingreturncodein operational scripts, troubleshooting becomes easier if you handle not onlyTimeoutExpiredbut also startupOSErrorseparately.stdoutandstderrare useful for root cause investigation, but do not determine success or failure based on them alone. Also, distinguish between the child process exit code and the exit code returned by the caller.
It is best to first observe the dummy 3-way branch, understand the behavior when changing the wait time, and then proceed to practical input validation, artifact verification, log protection, and re-execution design.
