1. ログも例外も無く消えた — この記事で扱う「無言終了」
※ 次の段落は説明のための架空の例です。
タスクスケジューラで回していたデータ収集スクリプトの出力が、いつの間にか止まっていた。ログの最終行はいつもの INFO で、Traceback は無い。schtasks で前回の結果を見ると -1073741819。検索しても「IDE を入れ直したら直った」という報告ばかりで、原因に近づけない。
本記事では、例外の記録を残さずにプロセスが終わることを「無言終了」と呼びます。中身は 3 種類です。
- 例外は起きたが、出力先が無くて見えない(pythonw・
--noconsoleの exe・別スレッド) - そもそも Python の例外にならない(C 拡張や DLL の中でのアクセス違反など)
- 外から止められた(
taskkill /F・Ctrl+C など)
どれに当たるかを最初に教えてくれるのが終了コード(プロセスが終わるときに OS へ返す数値。0 が正常)で、トレースバックが出せない死に方でも残ります。exe が起動しない症状は PyInstaller の逆引きガイド、本記事は「動いていたのに消えた」側です。
2. 終了コードの読み方 — 5 か所で読むと 2 通りに見える
同じクラッシュの終了コードを、5 つの場所から読みました(Python 3.12.10・python.exe)。
| 見る場所 | アクセス違反 | os.abort() | C 関数をはさむ再帰 | 未捕捉の KeyboardInterrupt | 未捕捉の例外 |
|---|---|---|---|---|---|
cmd の %ERRORLEVEL%・PowerShell 5.1 の $LASTEXITCODE | -1073741819 | -1073740791 | (未計測) | -1073741510 | 1 |
schtasks /Query /V の Last Result | -1073741819 | -1073740791 | -1073741571 | -1073741510 | 1 |
subprocess.run().returncode・Get-ScheduledTaskInfo の LastTaskResult | 3221225477 | 3221226505 | 3221225725 | 3221225786 | 1 |
| 16 進(NTSTATUS の名前) | 0xC0000005 ACCESS_VIOLATION | 0xC0000409 STACK_BUFFER_OVERRUN | 0xC00000FD STACK_OVERFLOW | 0xC000013A CONTROL_C_EXIT | 0x1 |
NTSTATUS は Windows の 32 ビットの状態コードで、先頭が 0xC のものはエラーです。KeyboardInterrupt の列は raise KeyboardInterrupt を捕まえずに終えた場合です。
2.1 負の数と 3221225477 は同じもの
違いは同じ 32 ビットの値を符号付きで読むか、符号なしで読むかだけで、符号付きに 4,294,967,296 を足すと符号なし(-1073741819 → 3221225477)、16 進 8 桁で書くと NTSTATUS(0xC0000005)です。検索は 16 進で行ってください。
# PowerShell(直前のコマンドの終了コード / タスクの前回の結果)
'0x{0:X8}' -f $LASTEXITCODE
Get-ScheduledTaskInfo -TaskName "タスク名" | ForEach-Object { '0x{0:X8}' -f $_.LastTaskResult }
# Python(r は subprocess.run(...) の結果)
print(f"0x{r.returncode & 0xFFFFFFFF:08X}")
2.2 タスクスケジューラと、自作ランチャーの注意
タスクスケジューラの記事の戻り値コード表には 0xC 系が無いので、上の表がその続きです(GUI の「前回の実行結果」列の表示は未採取)。
子の終了コードを親の Python で返すとき、sys.exit(3221225477) は OverflowError: Python int too large to convert to C long になり、0xFFFFFFFF(-1)に化けました。sys.exit(-1073741819) と符号付きにすれば 0xC0000005 のままです。
3. 無言終了の 3 種類 — 終了コードと「残るもの」
終わり方を 12 通り作り、終了コードと残ったものを並べました(3.12.10 の python.exe を PowerShell 配下の subprocess から起動)。ID 1000 はイベントログのクラッシュ記録です(4.3)。
| 終わり方 | returncode | 16 進 | stderr に残ったもの | ID 1000 |
|---|---|---|---|---|
| 未捕捉の例外 | 1 | 0x1 | トレースバック | 0 件 |
sys.exit("fatal: config not found") | 1 | 0x1 | メッセージ 1 行 | — |
sys.exit(2) | 2 | 0x2 | なし | — |
os._exit(3) | 3 | 0x3 | なし(atexit も走らない) | 0 件 |
os.abort() | 3221226505 | 0xC0000409 | なし | 1 件 |
| アクセス違反(3.2 の方法) | 3221225477 | 0xC0000005 | なし | 1 件 |
| C 関数をはさむ深い再帰 | 3221225725 | 0xC00000FD | なし | 1 件 |
未捕捉の KeyboardInterrupt | 3221225786 | 0xC000013A | トレースバック | 0 件 |
| 別スレッドの未捕捉例外 | 0 | 0x0 | トレースバック(メインは継続) | — |
Popen.terminate() | 1 | 0x1 | — | 0 件 |
taskkill /F /PID | 1 | 0x1 | — | 0 件 |
Stop-Process -Force | -1(.NET の表示) | 0xFFFFFFFF | — | 0 件 |
ID 1000 の「—」は未集計、強制終了の 3 行は PowerShell から止めた結果です。
終了コード 1 は例外とは限りません(taskkill /F でも 1)。終了コード 0 でも安心できません(別スレッドが死んでもメインが走り切れば 0)。
3.1 例外は起きたが、出力先が無い
pythonw.exe や --noconsole の exe では sys.stderr が None で、トレースバックの行き先がありません(既存記事で実測済み: タスクスケジューラ 6 章・tkinter 監視アプリ 6 章・logging 設計 7 章)。
3.2 OS レベルのクラッシュ — 0xC0000005・0xC0000409・0xC00000FD
0xC0000005(アクセス違反)は、触れてはいけないメモリに触れたときのコードです。純 Python の間違いは原則として例外になるので、疑うのは C 拡張や ctypes で呼ぶ DLL など C の部分です。
faulthandler の公式ドキュメントが Linux でのクラッシュ例に挙げる ctypes.string_at(0) は、検証機の Windows では落ちませんでした(実行結果)。
start mode=string_at0
caught OSError: exception: access violation reading 0x0000000000000000
still alive
end
ctypes のドキュメントによると、Windows の ctypes は外部関数の呼び出し中のアクセス違反を構造化例外処理(SEH)で OSError に変えます。この安全網の外で不正なポインタを読むと即死し、検証ではこの 1 行で再現しました。
import ctypes
ctypes.cast(1, ctypes.py_object).value # 不正な非 NULL ポインタを Python オブジェクトとして読む
stderr は空で、ID 1000 の障害モジュールは _ctypes.pyd でした。
0xC0000409 の名前は STACK_BUFFER_OVERRUN ですが、検証機では os.abort() でもこのコードになり、障害モジュールは ucrtbase.dll(C ランタイム)でした。Microsoft の __fastfail による即時終了もこのコードで現れるので、名前だけで決めつけないでください(5 章のコードの「fast-fail」は目安のラベル)。Python の公式ドキュメントの「os.abort() は Windows では終了コード 3」は、実測ではfaulthandler が有効なときだけで、無効なら 0xC0000409でした。faulthandler の SIGABRT ハンドラーがあると、UCRT の abort はそちらを呼んで WER を経ずに 3 で終わるためです(Microsoft Learn)。
0xC00000FD(スタックオーバーフロー)は、3.12.10 の純 Python の再帰では上限 100 万でも出ず RecursionError で止まり、落ちたのは再帰の途中に C で書かれた関数をはさんだときです。
"""再帰の途中に C 関数(sorted の key)を挟む。main スレッドで実行"""
import sys
def viaC(n):
if n % 100 == 0:
print(f"depth={n}", flush=True)
return sorted([0], key=lambda x: viaC(n + 1))
try:
viaC(0)
except RecursionError as e:
print(f"RecursionError: {e}", flush=True)
print("end", flush=True)
3.12.10 では depth=300 の表示を最後に 0xC00000FD で即死し、stderr は空でした。3.14.7 では例外として止まりました(別スレッドでも同じ結果。実行結果・該当部分)。
RecursionError: Stack overflow (used 2912 kB) while calling a Python object
end
3.3 os._exit・Ctrl+C・外からの停止
os._exit(n) は後始末をせずに終わり、ファイルバッファの書き出しも走りません。
未捕捉の KeyboardInterrupt は 0xC000013A で、.bat の中で子がこのコードで終わると Terminate batch job (Y/N)? が表示されました。
taskkill /F・Popen.terminate()・Stop-Process -Force で止められたプロセスは何も書けず、手掛かりは止めた側(運用者の操作・監視の仕組み・タスクの実行時間の上限=0x41306)にしかありません。
4. 原因を残す 3 段構え — excepthook・faulthandler・イベントログ
3 章の終わり方は記録できる場所が違うので、プロセスの内側に 2 段、外側に 1 段を重ね、親プロセスで終了コードも残します。
4.1 1 段目: excepthook — Python の例外
sys.excepthook と threading.excepthook を差し替え、未捕捉の例外をファイルに書きます。logging 設計の記事 7 章のフックと併用すると後から代入したほうが勝つので、どちらかに寄せてください。
4.2 2 段目: faulthandler — 例外にならない死
faulthandler は、落ちる直前に Python のスタックを書き出す標準ライブラリです(-X faulthandler・PYTHONFAULTHANDLER=1・faulthandler.enable(file=...) で有効化)。-X faulthandler を付けると、空だった stderr に次の内容が出ました(実行結果)。
Windows fatal exception: access violation
Current thread 0x00008434 (most recent call first):
File "C:\work\silent_exit\fh.py", line 7 in crash
File "C:\work\silent_exit\fh.py", line 54 in <module>
どの行で落ちたかが分かれば、その行が呼ぶライブラリが容疑者です。
enable(file=...) ではファイルを開いたままにする必要があります。with で閉じたあとに落とすと何も残らず、閉じたあとに別のファイルを開いてから落とすとその別のファイルに書かれました。faulthandler はファイル記述子の番号で書くためです(公式ドキュメントにも同趣旨の注意)。
固まる症状には faulthandler.dump_traceback_later(秒数, exit=True, file=f) が使え、3 秒指定で 30 秒眠る関数を動かすと、3 秒後のスタックを残して終了コード 1 で終わりました(実行結果)。
Timeout (0:00:03)!
Thread 0x00005a08 (most recent call first):
File "C:\work\silent_exit\fh.py", line 34 in stuck
File "C:\work\silent_exit\fh.py", line 35 in <module>
4.3 3 段目: イベントビューアー(Application・ID 1000)
OS レベルのクラッシュは、イベントログ(Application・ソース Application Error・ID 1000)にも残ります。PowerShell なら直近 1 日分をこう引けます。
Get-WinEvent -FilterHashtable @{
LogName = 'Application'
ProviderName = 'Application Error'
Id = 1000
StartTime = (Get-Date).AddDays(-1)
} -ErrorAction SilentlyContinue | Format-List TimeCreated, Message
検証機に残った本文の先頭です(実行結果・先頭 4 行)。
障害が発生しているアプリケーション名: pythonw.exe、バージョン: 3.12.10150.1013、タイム スタンプ: 0x67f515a8
障害が発生したモジュール名: _ctypes.pyd、 バージョン: 3.12.10150.1013、タイム スタンプ: 0x67f5157c
例外コード: 0xc0000005
フォールト オフセット: 0x0000000000004168
見るのは「障害が発生したモジュール名」で、検証ではアクセス違反が _ctypes.pyd、os.abort() が ucrtbase.dll、C 関数をはさむ再帰が python312.dll でした。ただし ntdll.dll など OS のモジュールは、他に壊された「被害者」のこともあります(Microsoft の文書)。
記録されたのは 3 章のクラッシュ 3 種だけで(pythonw.exe・タスクからの起動も記録)、未捕捉例外・KeyboardInterrupt・os._exit()・強制終了は 0 件でした。非公開 API の faulthandler._read_null() は内部でクラッシュ報告を抑えるので、0xC0000005 で落ちてもID 1000 が残りません。基本は exe-not-running の記事 2.5、本記事から足すのはID 1000 が無いことはクラッシュしていない証拠にならないという点です(6.3)。
4.4 さらに深く: WER のローカルダンプ
最後の手段は、クラッシュ時のメモリを保存する Windows エラー報告(WER)の LocalDumps です。Microsoft Learn「Collecting User-Mode Dumps」によると既定では無効で、設定(下のコマンドの HKLM のキー)には管理者権限が要り、既定は最大 10 個のミニダンプ(DumpType=2 でフルダンプ)です。検証機では別のアプリが作ったと見られるキーが値未設定のまますでにあり、本記事のクラッシュのミニダンプ(1 個 1.58〜2.00 MB)が10 個で頭打ち・古いものから上書きで残っていました。読み取りだけの確認は次の 2 行です。
Test-Path 'HKLM:\SOFTWARE\Microsoft\Windows\Windows Error Reporting\LocalDumps'
Get-ChildItem "$env:LOCALAPPDATA\CrashDumps"
5. 死因をファイルに残す run_with_crash_log.py(93 行)
4 章の 1 段目・2 段目と終了コードの記録を、標準ライブラリだけの 1 ファイルにまとめました。pip が使えない閉域網の PC にもコピーで持ち込め、管理者権限も要りません。
- アプリ側: 先頭で
install()を呼ぶと crash.log(例外と atexit の印)と fault.log ができる(exe でも同じ) - 起動側(ランチャー): このファイルからアプリを子として起動し、終了コードを 16 進と意味つきで exit.log に残してそのまま返す
"""run_with_crash_log.py — 無言終了の「死因」をファイルに残す(標準ライブラリのみ)
使い方 1(アプリ側): main の先頭で install() を呼ぶ。exe(PyInstaller)でも同じ。
import run_with_crash_log
run_with_crash_log.install("logs")
使い方 2(起動側): 子プロセスとして起動し、終了コードを 16 進と意味で記録する。
python run_with_crash_log.py logs python app.py
"""
import atexit
import datetime
import faulthandler
import os
import subprocess
import sys
import threading
import traceback
# 終了コード(符号なし 32 ビット)→ 意味。よく見るものだけ
KNOWN_CODES = {
0x00000000: "正常終了",
0x00000001: "Python の未捕捉例外 / sys.exit(1) / taskkill /F などの強制終了",
0x00000003: "os._exit(3) / faulthandler 有効時の os.abort()",
0xFFFFFFFF: "Stop-Process -Force などの強制終了(-1)",
0xC0000005: "アクセス違反(C 拡張・ctypes・DLL の中で落ちた)",
0xC00000FD: "スタックオーバーフロー(C 関数をはさむ深い再帰など)",
0xC0000409: "fast-fail(os.abort() や C ランタイムの abort() など)",
0xC000013A: "Ctrl+C / コンソールを閉じた(KeyboardInterrupt 未捕捉を含む)",
0xC0000374: "ヒープ破損",
}
_fault_file = None # faulthandler の出力先。閉じると記録されなくなるので保持し続ける
def _now():
return datetime.datetime.now().isoformat(timespec="seconds")
def _write(path, text):
with open(path, "a", encoding="utf-8") as f:
f.write(f"{_now()} pid={os.getpid()} {text}\n")
def install(log_dir="logs"):
"""excepthook 2 つと faulthandler をまとめて仕掛ける"""
global _fault_file
os.makedirs(log_dir, exist_ok=True)
crash_log = os.path.join(log_dir, "crash.log")
def on_exception(exc_type, exc, tb): # 1 段目: メインスレッドの未捕捉例外
_write(crash_log, "UNCAUGHT\n" + "".join(traceback.format_exception(exc_type, exc, tb)))
if sys.stderr is not None: # pythonw / --noconsole では None
sys.__excepthook__(exc_type, exc, tb)
def on_thread_exception(args): # 1 段目(続き): 別スレッドの未捕捉例外
name = args.thread.name if args.thread else "?"
_write(crash_log, f"THREAD {name}\n"
+ "".join(traceback.format_exception(args.exc_type, args.exc_value, args.exc_traceback)))
sys.excepthook = on_exception
threading.excepthook = on_thread_exception
# 2 段目: 例外にならない死(0xC0000005 など)。追記モードで開き、閉じずに持つ
_fault_file = open(os.path.join(log_dir, "fault.log"), "a", encoding="utf-8")
_fault_file.write(f"--- {_now()} pid={os.getpid()} start {sys.argv}\n")
_fault_file.flush()
faulthandler.enable(file=_fault_file, all_threads=True)
# 正常な後始末まで来たら印を残す(os._exit・強制終了・クラッシュでは書かれない)
atexit.register(_write, crash_log, "atexit reached")
def describe(code):
u = code & 0xFFFFFFFF
return f"{code} (0x{u:08X}) {KNOWN_CODES.get(u, '不明(NTSTATUS 一覧で 16 進を検索)')}"
def launch(log_dir, cmd):
"""子プロセスとして起動し、終了コードを exit.log に 1 行で残す"""
os.makedirs(log_dir, exist_ok=True)
exit_log = os.path.join(log_dir, "exit.log")
_write(exit_log, f"START {cmd}")
rc = subprocess.run(cmd).returncode
_write(exit_log, f"EXIT {describe(rc)}")
return rc
if __name__ == "__main__":
if len(sys.argv) < 3:
print("usage: python run_with_crash_log.py LOG_DIR COMMAND [ARGS...]")
sys.exit(2)
rc = launch(sys.argv[1], sys.argv[2:])
# 子の終了コードをそのまま返す。3221225477 のままだと OverflowError になるので符号付きへ
sys.exit(rc - (1 << 32) if rc >= (1 << 31) else rc)
要所は、_fault_file を閉じずに持ち続けること(4.2)、sys.stderr が None なら元のフックを呼ばないこと(pythonw 対策)、子の終了コードを符号付きで返すこと(2.2)です。
5.1 動かし方
動作確認に使った検証用アプリです。av と abort はプロセスを即死させるので、3.2 の注意を守ってください。
"""検証用アプリ: install() を呼んだうえで、引数のモードで落ちる"""
import sys, os, ctypes, threading, time
import run_with_crash_log
run_with_crash_log.install(os.environ.get("LOGDIR", "logs"))
mode = sys.argv[1] if len(sys.argv) > 1 else "ok"
print(f"demo start mode={mode}", flush=True)
if mode == "uncaught":
1 / 0
elif mode == "thread":
t = threading.Thread(target=lambda: int("x"), name="collector"); t.start(); t.join()
elif mode == "av":
ctypes.cast(1, ctypes.py_object).value
elif mode == "os_exit":
os._exit(3)
elif mode == "abort":
os.abort()
print("demo end", flush=True)
2 つを同じフォルダに置き、ランチャー経由で起動します(第 1 引数がログフォルダ、以降が子のコマンド)。
python run_with_crash_log.py logs python demo_app.py av
ログフォルダは絶対パスで指定してください(install() に渡すフォルダも同じ)。タスクスケジューラ起動では作業フォルダが System32 になります(タスクスケジューラの記事 4 章)。
5.2 実際に残ったログ
PowerShell 5.1 から 6 通りの落とし方で起動すると、ランチャーの終了コードは ok 0・uncaught 1・thread 0・av -1073741819・os_exit 3・abort 3(faulthandler 有効のため)で、子の値を引き継ぎました。exit.log の av と os_exit の行です(実行結果・抜粋)。
2026-10-09T15:39:49 pid=16484 START ['C:\\Users\\<USER>\\AppData\\Local\\Programs\\Python\\Python312\\python.exe', 'demo_app.py', 'av']
2026-10-09T15:39:52 pid=16484 EXIT 3221225477 (0xC0000005) アクセス違反(C 拡張・ctypes・DLL の中で落ちた)
2026-10-09T15:39:52 pid=21640 START ['C:\\Users\\<USER>\\AppData\\Local\\Programs\\Python\\Python312\\python.exe', 'demo_app.py', 'os_exit']
2026-10-09T15:39:52 pid=21640 EXIT 3 (0x00000003) os._exit(3) / faulthandler 有効時の os.abort()
fault.log の av 以降です(実行結果・抜粋)。crash.log には uncaught のトレースバックと atexit 行が残りました。
--- 2026-10-09T15:39:49 pid=35648 start ['demo_app.py', 'av']
Windows fatal exception: access violation
Current thread 0x00001070 (most recent call first):
File "C:\work\silent_exit\demo_app.py", line 12 in <module>
--- 2026-10-09T15:39:52 pid=2712 start ['demo_app.py', 'os_exit']
--- 2026-10-09T15:39:52 pid=22924 start ['demo_app.py', 'abort']
Fatal Python error: Aborted
Current thread 0x00006c64 (most recent call first):
File "C:\work\silent_exit\demo_app.py", line 16 in <module>
exit.log の pid はランチャー、crash.log・fault.log の pid はアプリのものなので、突き合わせは時刻で行ってください。
pythonw.exe の直接起動と PyInstaller 6.22.3 の --noconsole の exe(onedir・onefile 各 1 本。ほかの 2 本は Smart App Control がブロック。対処はコード署名の記事)でも、ログは同じ形で残りました。
5.3 ログの読み方 — 「何が無いか」も手掛かりになる
| fault.log(その pid) | crash.log | 終了コード | 読み方 |
|---|---|---|---|
start → Windows fatal exception: … | atexit なし | 0xC0000005 など | ネイティブのクラッシュ。最後の File 行と ID 1000 の障害モジュールを見る |
start → Fatal Python error: Aborted | atexit なし | 3 | os.abort() か、C 側での abort |
| start だけ | UNCAUGHT + atexit | 1 | Python の未捕捉例外 |
| start だけ | THREAD + atexit | 0 | 別スレッドだけが死に、メインは走り切った |
| start だけ | atexit なし | 3 など | os._exit()。強制終了も同じ形になるはず(未確認) |
| start だけ | atexit だけ | 0 | 正常終了 |
4 行目は、タスク上は成功なのに収集スレッドが止まっている、いちばん気づきにくい形です。
6. 落とし穴 — 記録が当てにならない 4 つの場面
6.1 pythonw に PYTHONFAULTHANDLER=1 を付けると、1 行も動かない
Start-Process で標準ハンドルを渡さずに pythonw.exe を起動し、-X faulthandler か PYTHONFAULTHANDLER=1 を付けると、スクリプトが 1 行も実行されずに終了コード 1 で終わりました。file 省略の faulthandler.enable() は RuntimeError: sys.stderr is None になり、enable(file=開いたままのファイル) なら pythonw でもダンプが残ります。
パイプ付きの subprocess から起動すると sys.stderr が None にならず、問題なく動きました。起動のしかたで結果が変わるので、PYTHONFAULTHANDLER は全体設定に使わず、python.exe で調べるときだけ付けるのが安全です。
6.2 exe の未捕捉例外は「落ちずに止まる」
--noconsole の exe で未捕捉例外が起きると、crash.log に UNCAUGHT が書かれたあと PyInstaller の Unhandled exception in script ダイアログが出て、閉じるまでプロセスが残りました。プロセス監視では正常に見えるので、処理結果の更新時刻も見張ってください(exe-not-running の記事 3.2)。
6.3 Git Bash から起動すると ID 1000 が残らない
3 章のクラッシュを含む 7 ケースを Git Bash から起動すると、ID 1000 は1 件も記録されませんでした(PowerShell からは 3 件)。起動された python.exe のエラーモード(GetErrorMode())は、Git Bash 配下が 0x3、PowerShell 配下が 0x8001 でした。
0x3 には SEM_NOGPFAULTERRORBOX(0x2)が含まれ、Microsoft Learn の SetErrorMode の説明では、このフラグがあると Windows エラー報告が呼ばれず、子プロセスは親のエラーモードを引き継ぎます。次の 1 行の結果に0x2 のビットが含まれていれば、その起動経路では ID 1000 を当てにできません。
python -c "import ctypes; print(hex(ctypes.windll.kernel32.GetErrorMode()))"
6.4 「Windows fatal exception」が出ても死んでいないことがある
faulthandler を有効にして 3.2 の ctypes.string_at(0) を実行すると、ctypes が例外に変えて生き延びた(終了コード 0)のに、stderr には Windows fatal exception: access violation とスタックが出ました(faulthandler が安全網より先に呼ばれるため)。fault.log にこの行があっても死因と決めず、同じ pid の atexit 行が crash.log にあるかを見てください。逆に、SAC にブロックされた exe はログ自体が作られず、「ログが空」には「起動していない」も含まれます。
7. よくある質問(FAQ)
「Process finished with exit code -1073741819 (0xC0000005)」と出ました。何から調べればよいですか
Python の例外ではなく、アクセス違反で落ちたことを示す終了コードです(0xC0000005 の符号付き表示)。入れ直しの前に、python -X faulthandler で落ちた行を出し、ID 1000 の「障害が発生したモジュール名」を見て、C 拡張や DLL の当たりを付けてください(4.2・4.3)。
-1073740791(0xC0000409)はバッファオーバーランという意味ですか
名前は STACK_BUFFER_OVERRUN ですが、当サイトの検証機では os.abort() でもこのコードになりました。名前で決めつけず、ID 1000 の障害モジュール(検証機では ucrtbase.dll)を見てください(3.2)。
再帰で 0xC00000FD になるときと、RecursionError で止まるときの違いは何ですか
当サイトの検証機の 3.12.10 では、純 Python の再帰は RecursionError で止まり、sorted の key のような C 関数をはさんだときだけ 0xC00000FD で即死しました(3.14.7 では RecursionError・3.2)。
PYTHONFAULTHANDLER=1 を環境変数に設定しておけば安心ですか
pythonw.exe には危険です。当サイトの検証機では標準ハンドルなしの pythonw.exe に付けるとスクリプトが 1 行も動かなかったので、コード内で faulthandler.enable(file=開いたままのファイル) を呼んでください(6.1)。
イベントビューアーに ID 1000 が無ければ、クラッシュではないと言えますか
言えません。当サイトの検証機では Git Bash から起動したクラッシュの ID 1000 が 1 件も残らなかったので(6.3)、終了コードを親プロセス側で記録してください(5 章)。
8. 検証環境と、当サイトが測れなかったこと
| 実測した | 実測していない(当サイトで未確認という意味) |
|---|---|
| 終了コードの 5 か所での見え方(一時タスクは確認後に削除) | タスクスケジューラ GUI の「前回の実行結果」列の表示。0xC00000FD の cmd・PowerShell での表示 |
| 3 章の 12 通りの終わり方 | 0xC0000374(ヒープ破損)・DLL 系(0xC0000135 / 0xC0000142)、実在の C 拡張のバージョン不一致、コード中の「コンソールを閉じた」 |
| faulthandler(3.12.10・3.14.7)、ID 1000(PowerShell・Git Bash・タスク・pythonw) | WER LocalDumps の有効化・ダンプの解析(レジストリは変更していない) |
5 章のコード(ランチャー・pythonw・--noconsole の exe 2 本) | タスク登録での通し確認、taskkill /F 時のログ、Windows サービス・SYSTEM アカウント、他の PC・Windows 10 |
検証環境: 自宅の検証機(Windows 11 Pro 25H2・ビルド 26200・Smart App Control On)。会社支給の端末は使っていません。 Python 3.12.10(64bit)と比較用の 3.14.7、導入済みの PyInstaller 6.22.3。PowerShell 5.1・cmd・Git Bash。標準ライブラリのみで、レジストリ・システム設定の変更なし、管理者権限なし。ログ中のパスは C:\work\silent_exit、ユーザー名は <USER> に置き換えています。計測日は 2026 年 10 月 9 日です。
⚠️ 実機適用時の注意: クラッシュ再現コードは稼働中の設備に接続した PC では実行しないでください。記録の仕組みを現場 PC へ入れるときは、①設備保全部門・情報システム部門の承認を得る、②24 時間稼働している PLC や設備には接続せず、計画停止中か読み出し専用の検証環境で確かめる、③安全インターロック・安全 PLC・法定の警報装置は Python アプリで置き換えない、④レジストリの変更は情報システム部門の管轄として扱う——が前提です。適用による設備の停止・データ欠損・機会損失について、筆者・GenbaPy は責任を負いません。
Python は Python Software Foundation(PSF)の商標です。本記事は PSF およびその関連団体とは無関係の第三者による解説です。その他の製品名・会社名(Microsoft・Windows・Smart App Control・PyInstaller・Git など)は、各社または各プロジェクトの商標または登録商標です(利用規約の商標に関する条項)。本サイトの AI 利用方針はサイトについてに記載しています。